Add the Flex EG
This commit is contained in:
parent
bf0786ca63
commit
b692377f7b
17 changed files with 717 additions and 3 deletions
3
dpf.mk
3
dpf.mk
|
|
@ -66,6 +66,8 @@ SFIZZ_SOURCES = \
|
||||||
src/sfizz/modulations/ModKeyHash.cpp \
|
src/sfizz/modulations/ModKeyHash.cpp \
|
||||||
src/sfizz/modulations/ModMatrix.cpp \
|
src/sfizz/modulations/ModMatrix.cpp \
|
||||||
src/sfizz/modulations/sources/Controller.cpp \
|
src/sfizz/modulations/sources/Controller.cpp \
|
||||||
|
src/sfizz/modulations/sources/FlexEGDescription.cpp \
|
||||||
|
src/sfizz/modulations/sources/FlexEnvelope.cpp \
|
||||||
src/sfizz/modulations/sources/LFO.cpp \
|
src/sfizz/modulations/sources/LFO.cpp \
|
||||||
src/sfizz/effects/Compressor.cpp \
|
src/sfizz/effects/Compressor.cpp \
|
||||||
src/sfizz/effects/Disto.cpp \
|
src/sfizz/effects/Disto.cpp \
|
||||||
|
|
@ -91,6 +93,7 @@ SFIZZ_SOURCES = \
|
||||||
src/sfizz/FileMetadata.cpp \
|
src/sfizz/FileMetadata.cpp \
|
||||||
src/sfizz/FilePool.cpp \
|
src/sfizz/FilePool.cpp \
|
||||||
src/sfizz/FilterPool.cpp \
|
src/sfizz/FilterPool.cpp \
|
||||||
|
src/sfizz/FlexEnvelope.cpp \
|
||||||
src/sfizz/FloatEnvelopes.cpp \
|
src/sfizz/FloatEnvelopes.cpp \
|
||||||
src/sfizz/Logger.cpp \
|
src/sfizz/Logger.cpp \
|
||||||
src/sfizz/LFO.cpp \
|
src/sfizz/LFO.cpp \
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ set (SFIZZ_HEADERS
|
||||||
sfizz/modulations/ModMatrix.h
|
sfizz/modulations/ModMatrix.h
|
||||||
sfizz/modulations/ModGenerator.h
|
sfizz/modulations/ModGenerator.h
|
||||||
sfizz/modulations/sources/Controller.h
|
sfizz/modulations/sources/Controller.h
|
||||||
|
sfizz/modulations/sources/FlexEnvelope.h
|
||||||
sfizz/modulations/sources/LFO.h
|
sfizz/modulations/sources/LFO.h
|
||||||
sfizz/effects/impl/ResonantArray.h
|
sfizz/effects/impl/ResonantArray.h
|
||||||
sfizz/effects/impl/ResonantArrayAVX.h
|
sfizz/effects/impl/ResonantArrayAVX.h
|
||||||
|
|
@ -67,6 +68,8 @@ set (SFIZZ_HEADERS
|
||||||
sfizz/FilePool.h
|
sfizz/FilePool.h
|
||||||
sfizz/FilterDescription.h
|
sfizz/FilterDescription.h
|
||||||
sfizz/FilterPool.h
|
sfizz/FilterPool.h
|
||||||
|
sfizz/FlexEGDescription.h
|
||||||
|
sfizz/FlexEnvelope.h
|
||||||
sfizz/HistoricalBuffer.h
|
sfizz/HistoricalBuffer.h
|
||||||
sfizz/Interpolators.h
|
sfizz/Interpolators.h
|
||||||
sfizz/Interpolators.hpp
|
sfizz/Interpolators.hpp
|
||||||
|
|
@ -139,11 +142,14 @@ set (SFIZZ_SOURCES
|
||||||
sfizz/LFO.cpp
|
sfizz/LFO.cpp
|
||||||
sfizz/LFODescription.cpp
|
sfizz/LFODescription.cpp
|
||||||
sfizz/PowerFollower.cpp
|
sfizz/PowerFollower.cpp
|
||||||
|
sfizz/FlexEGDescription.cpp
|
||||||
|
sfizz/FlexEnvelope.cpp
|
||||||
sfizz/modulations/ModId.cpp
|
sfizz/modulations/ModId.cpp
|
||||||
sfizz/modulations/ModKey.cpp
|
sfizz/modulations/ModKey.cpp
|
||||||
sfizz/modulations/ModKeyHash.cpp
|
sfizz/modulations/ModKeyHash.cpp
|
||||||
sfizz/modulations/ModMatrix.cpp
|
sfizz/modulations/ModMatrix.cpp
|
||||||
sfizz/modulations/sources/Controller.cpp
|
sfizz/modulations/sources/Controller.cpp
|
||||||
|
sfizz/modulations/sources/FlexEnvelope.cpp
|
||||||
sfizz/modulations/sources/LFO.cpp
|
sfizz/modulations/sources/LFO.cpp
|
||||||
sfizz/utility/SpinMutex.cpp
|
sfizz/utility/SpinMutex.cpp
|
||||||
sfizz/effects/Nothing.cpp
|
sfizz/effects/Nothing.cpp
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,13 @@ Curve Curve::buildBipolar(float v1, float v2)
|
||||||
return curve;
|
return curve;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Curve Curve::buildFromPoints(const float points[NumValues])
|
||||||
|
{
|
||||||
|
Curve curve;
|
||||||
|
copy(absl::MakeConstSpan(points, NumValues), absl::Span<float>(curve._points));
|
||||||
|
return curve;
|
||||||
|
}
|
||||||
|
|
||||||
const Curve& Curve::getDefault()
|
const Curve& Curve::getDefault()
|
||||||
{
|
{
|
||||||
return defaultCurve;
|
return defaultCurve;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@ struct Opcode;
|
||||||
*/
|
*/
|
||||||
class Curve {
|
class Curve {
|
||||||
public:
|
public:
|
||||||
|
enum { NumValues = 128 };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Compute the curve for integral x in domain [0:127]
|
* @brief Compute the curve for integral x in domain [0:127]
|
||||||
*/
|
*/
|
||||||
|
|
@ -93,14 +95,16 @@ public:
|
||||||
*/
|
*/
|
||||||
static Curve buildBipolar(float v1, float v2);
|
static Curve buildBipolar(float v1, float v2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Build a curve from a table of points
|
||||||
|
*/
|
||||||
|
static Curve buildFromPoints(const float points[NumValues]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get a linear curve from 0 to 1
|
* @brief Get a linear curve from 0 to 1
|
||||||
*/
|
*/
|
||||||
static const Curve& getDefault();
|
static const Curve& getDefault();
|
||||||
|
|
||||||
private:
|
|
||||||
enum { NumValues = 128 };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fill(Interpolator itp, const bool fillStatus[NumValues]);
|
void fill(Interpolator itp, const bool fillStatus[NumValues]);
|
||||||
void lerpFill(const bool fillStatus[NumValues]);
|
void lerpFill(const bool fillStatus[NumValues]);
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,20 @@ namespace Default
|
||||||
constexpr Range<float> egOnCCTimeRange { -100.0, 100.0 };
|
constexpr Range<float> egOnCCTimeRange { -100.0, 100.0 };
|
||||||
constexpr Range<float> egOnCCPercentRange { -100.0, 100.0 };
|
constexpr Range<float> egOnCCPercentRange { -100.0, 100.0 };
|
||||||
|
|
||||||
|
// Flex envelope generators
|
||||||
|
constexpr int numFlexEGs { 4 };
|
||||||
|
constexpr int numFlexEGPoints { 8 };
|
||||||
|
constexpr int flexEGDynamic { 0 };
|
||||||
|
constexpr int flexEGSustain { 0 };
|
||||||
|
constexpr float flexEGPointTime { 0 };
|
||||||
|
constexpr float flexEGPointLevel { 0 };
|
||||||
|
constexpr float flexEGPointShape { 0 };
|
||||||
|
constexpr Range<int> flexEGDynamicRange { 0, 1 };
|
||||||
|
constexpr Range<int> flexEGSustainRange { 0, 100 };
|
||||||
|
constexpr Range<float> flexEGPointTimeRange { 0.0f, 100.0f };
|
||||||
|
constexpr Range<float> flexEGPointLevelRange { -1.0f, 1.0f };
|
||||||
|
constexpr Range<float> flexEGPointShapeRange { -100.0f, 100.0f };
|
||||||
|
|
||||||
// ***** SFZ v2 ********
|
// ***** SFZ v2 ********
|
||||||
constexpr int sampleQuality { 1 };
|
constexpr int sampleQuality { 1 };
|
||||||
constexpr int sampleQualityInFreewheelingMode { 10 }; // for future use, possibly excessive
|
constexpr int sampleQualityInFreewheelingMode { 10 }; // for future use, possibly excessive
|
||||||
|
|
|
||||||
87
src/sfizz/FlexEGDescription.cpp
Normal file
87
src/sfizz/FlexEGDescription.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
// 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 "FlexEGDescription.h"
|
||||||
|
#include "Curve.h"
|
||||||
|
#include <absl/container/flat_hash_map.h>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
|
||||||
|
void FlexEGPoint::setShape(float shape)
|
||||||
|
{
|
||||||
|
shape_ = shape;
|
||||||
|
shapeCurve_ = FlexEGs::getShapeCurve(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Curve& FlexEGPoint::curve() const
|
||||||
|
{
|
||||||
|
if (shapeCurve_)
|
||||||
|
return *shapeCurve_;
|
||||||
|
else
|
||||||
|
return Curve::getDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
typedef absl::flat_hash_map<float, std::weak_ptr<Curve>> FlexEGShapes;
|
||||||
|
|
||||||
|
static FlexEGShapes& getShapeMap()
|
||||||
|
{
|
||||||
|
static FlexEGShapes shapes;
|
||||||
|
return shapes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Curve> FlexEGs::getShapeCurve(float shape)
|
||||||
|
{
|
||||||
|
static FlexEGShapes& map = getShapeMap();
|
||||||
|
|
||||||
|
std::weak_ptr<Curve>& slot = map[shape];
|
||||||
|
|
||||||
|
std::shared_ptr<Curve> curve = slot.lock();
|
||||||
|
if (curve)
|
||||||
|
return curve;
|
||||||
|
|
||||||
|
curve.reset(new Curve);
|
||||||
|
|
||||||
|
///
|
||||||
|
constexpr unsigned numPoints = Curve::NumValues;
|
||||||
|
float points[numPoints];
|
||||||
|
|
||||||
|
if (shape == 0)
|
||||||
|
*curve = Curve::getDefault();
|
||||||
|
else if (shape > 0) {
|
||||||
|
for (unsigned i = 0; i < numPoints; ++i) {
|
||||||
|
float x = float(i) / (numPoints - 1);
|
||||||
|
points[i] = std::pow(x, shape);
|
||||||
|
}
|
||||||
|
*curve = Curve::buildFromPoints(points);
|
||||||
|
}
|
||||||
|
else if (shape < 0) {
|
||||||
|
for (unsigned i = 0; i < numPoints; ++i) {
|
||||||
|
float x = float(i) / (numPoints - 1);
|
||||||
|
points[i] = 1 - std::pow(1 - x, -shape);
|
||||||
|
}
|
||||||
|
*curve = Curve::buildFromPoints(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
slot = curve;
|
||||||
|
return curve;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEGs::clearUnusedCurves()
|
||||||
|
{
|
||||||
|
static FlexEGShapes& map = getShapeMap();
|
||||||
|
|
||||||
|
for (auto it = map.begin(); it != map.end(); ) {
|
||||||
|
if (it->second.use_count() == 0)
|
||||||
|
map.erase(it++);
|
||||||
|
else
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
39
src/sfizz/FlexEGDescription.h
Normal file
39
src/sfizz/FlexEGDescription.h
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 "Defaults.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
class Curve;
|
||||||
|
|
||||||
|
namespace FlexEGs {
|
||||||
|
std::shared_ptr<Curve> getShapeCurve(float shape);
|
||||||
|
void clearUnusedCurves();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FlexEGPoint {
|
||||||
|
float time { Default::flexEGPointTime }; // duration until next step (s)
|
||||||
|
float level { Default::flexEGPointLevel }; // normalized amplitude
|
||||||
|
|
||||||
|
void setShape(float shape);
|
||||||
|
float shape() const noexcept { return shape_; }
|
||||||
|
const Curve& curve() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
float shape_ { Default::flexEGPointShape }; // 0: linear, positive: exp, negative: log
|
||||||
|
std::shared_ptr<Curve> shapeCurve_;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FlexEGDescription {
|
||||||
|
int dynamic { Default::flexEGDynamic }; // whether parameters can be modulated while EG runs
|
||||||
|
int sustain { Default::flexEGSustain }; // index of the sustain point (default to 0 in ARIA)
|
||||||
|
std::vector<FlexEGPoint> points;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
206
src/sfizz/FlexEnvelope.cpp
Normal file
206
src/sfizz/FlexEnvelope.cpp
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
/*
|
||||||
|
Note(jpc): implementation status
|
||||||
|
|
||||||
|
- [ ] egN_points (purpose unknown)
|
||||||
|
- [x] egN_timeX
|
||||||
|
- [x] egN_levelX
|
||||||
|
- [x] egN_shapeX
|
||||||
|
- [x] egN_sustain
|
||||||
|
- [ ] egN_dynamic
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "FlexEnvelope.h"
|
||||||
|
#include "FlexEGDescription.h"
|
||||||
|
#include "Curve.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "SIMDHelpers.h"
|
||||||
|
#include <absl/types/optional.h>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
|
||||||
|
struct FlexEnvelope::Impl {
|
||||||
|
const FlexEGDescription* desc_ { nullptr };
|
||||||
|
float samplePeriod_ { 1.0 / config::defaultSampleRate };
|
||||||
|
size_t delayFramesLeft_ { 0 };
|
||||||
|
|
||||||
|
//
|
||||||
|
float stageSourceLevel_ { 0.0 };
|
||||||
|
float stageTargetLevel_ { 0.0 };
|
||||||
|
float stageTime_ { 0.0 };
|
||||||
|
bool stageSustained_ { false };
|
||||||
|
const Curve* stageCurve_ { nullptr };
|
||||||
|
|
||||||
|
//
|
||||||
|
unsigned currentStageNumber_ { 0 };
|
||||||
|
float currentLevel_ { 0.0 };
|
||||||
|
float currentTime_ { 0.0 };
|
||||||
|
absl::optional<size_t> currentFramesUntilRelease_ { absl::nullopt };
|
||||||
|
bool isReleased_ { false };
|
||||||
|
|
||||||
|
//
|
||||||
|
void process(absl::Span<float> out);
|
||||||
|
bool advanceToNextStage();
|
||||||
|
};
|
||||||
|
|
||||||
|
FlexEnvelope::FlexEnvelope()
|
||||||
|
: impl_(new Impl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexEnvelope::~FlexEnvelope()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::setSampleRate(double sampleRate)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
impl.samplePeriod_ = 1.0 / sampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::configure(const FlexEGDescription* desc)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
impl.desc_ = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::start(unsigned triggerDelay)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
const FlexEGDescription& desc = *impl.desc_;
|
||||||
|
|
||||||
|
impl.delayFramesLeft_ = triggerDelay;
|
||||||
|
|
||||||
|
FlexEGPoint point;
|
||||||
|
if (!desc.points.empty())
|
||||||
|
point = desc.points[0];
|
||||||
|
|
||||||
|
//
|
||||||
|
impl.stageSourceLevel_ = 0.0;
|
||||||
|
impl.stageTargetLevel_ = point.level;
|
||||||
|
impl.stageTime_ = point.time;
|
||||||
|
impl.stageSustained_ = desc.sustain == 0;
|
||||||
|
impl.stageCurve_ = &point.curve();
|
||||||
|
impl.currentFramesUntilRelease_ = absl::nullopt;
|
||||||
|
impl.isReleased_ = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
impl.currentStageNumber_ = 0;
|
||||||
|
impl.currentLevel_ = 0.0;
|
||||||
|
impl.currentTime_ = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::release(unsigned releaseDelay)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
impl.currentFramesUntilRelease_ = releaseDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::process(absl::Span<float> out)
|
||||||
|
{
|
||||||
|
Impl& impl = *impl_;
|
||||||
|
impl.process(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::Impl::process(absl::Span<float> out)
|
||||||
|
{
|
||||||
|
const FlexEGDescription& desc = *desc_;
|
||||||
|
size_t numFrames = out.size();
|
||||||
|
const float samplePeriod = samplePeriod_;
|
||||||
|
|
||||||
|
// Skip the initial delay, for frame-accurate trigger
|
||||||
|
size_t skipFrames = std::min(numFrames, delayFramesLeft_);
|
||||||
|
if (skipFrames > 0) {
|
||||||
|
delayFramesLeft_ -= skipFrames;
|
||||||
|
fill(absl::MakeSpan(out.data(), skipFrames), 0.0f);
|
||||||
|
out.remove_prefix(skipFrames);
|
||||||
|
numFrames -= skipFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Envelope finished?
|
||||||
|
if (currentStageNumber_ >= desc.points.size()) {
|
||||||
|
fill(out, 0.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t frameIndex = 0;
|
||||||
|
absl::optional<size_t> framesUntilRelease = currentFramesUntilRelease_;
|
||||||
|
|
||||||
|
while (frameIndex < numFrames) {
|
||||||
|
// Check for release
|
||||||
|
if (framesUntilRelease && *framesUntilRelease == 0) {
|
||||||
|
isReleased_ = true;
|
||||||
|
framesUntilRelease = absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform stage transitions
|
||||||
|
const bool isReleased = isReleased_;
|
||||||
|
while ((!stageSustained_ && currentTime_ >= stageTime_) ||
|
||||||
|
(stageSustained_ && isReleased)) {
|
||||||
|
if (!advanceToNextStage()) {
|
||||||
|
fill(out, 0.0f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process without going past the release point, if there is one
|
||||||
|
size_t maxFrameIndex = numFrames;
|
||||||
|
if (framesUntilRelease)
|
||||||
|
maxFrameIndex = std::min(maxFrameIndex, frameIndex + *framesUntilRelease);
|
||||||
|
|
||||||
|
// Process the current stage
|
||||||
|
float time = currentTime_;
|
||||||
|
float level = currentLevel_;
|
||||||
|
const float stageEndTime = stageTime_;
|
||||||
|
const float sourceLevel = stageSourceLevel_;
|
||||||
|
const float targetLevel = stageTargetLevel_;
|
||||||
|
const bool sustained = stageSustained_;
|
||||||
|
const Curve& curve = *stageCurve_;
|
||||||
|
size_t framesDone = 0;
|
||||||
|
while ((time < stageEndTime || sustained) && frameIndex < maxFrameIndex) {
|
||||||
|
float x = time * (1.0f / stageEndTime);
|
||||||
|
float c = curve.evalNormalized(x);
|
||||||
|
level = sourceLevel + c * (targetLevel - sourceLevel);
|
||||||
|
out[frameIndex++] = level;
|
||||||
|
time += samplePeriod;
|
||||||
|
++framesDone;
|
||||||
|
}
|
||||||
|
currentLevel_ = level;
|
||||||
|
|
||||||
|
// Update the counter to release
|
||||||
|
if (framesUntilRelease)
|
||||||
|
*framesUntilRelease -= framesDone;
|
||||||
|
|
||||||
|
currentTime_ = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentFramesUntilRelease_ = framesUntilRelease;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlexEnvelope::Impl::advanceToNextStage()
|
||||||
|
{
|
||||||
|
const FlexEGDescription& desc = *desc_;
|
||||||
|
|
||||||
|
unsigned nextStageNo = currentStageNumber_ + 1;
|
||||||
|
currentStageNumber_ = nextStageNo;
|
||||||
|
|
||||||
|
if (currentStageNumber_ >= desc.points.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const FlexEGPoint& point = desc.points[currentStageNumber_];
|
||||||
|
stageSourceLevel_ = currentLevel_;
|
||||||
|
stageTargetLevel_ = point.level;
|
||||||
|
stageTime_ = point.time;
|
||||||
|
stageSustained_ = int(nextStageNo) == desc.sustain;
|
||||||
|
stageCurve_ = &point.curve();
|
||||||
|
|
||||||
|
currentTime_ = 0;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
53
src/sfizz/FlexEnvelope.h
Normal file
53
src/sfizz/FlexEnvelope.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// 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 <absl/types/span.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
struct FlexEGDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Flex envelope generator (according to ARIA)
|
||||||
|
*/
|
||||||
|
class FlexEnvelope {
|
||||||
|
public:
|
||||||
|
FlexEnvelope();
|
||||||
|
~FlexEnvelope();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sets the sample rate.
|
||||||
|
*/
|
||||||
|
void setSampleRate(double sampleRate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Attach some control parameters to this EG.
|
||||||
|
The control structure is owned by the caller.
|
||||||
|
*/
|
||||||
|
void configure(const FlexEGDescription* desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Start processing an EG as a region is triggered.
|
||||||
|
*/
|
||||||
|
void start(unsigned triggerDelay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Release the EG.
|
||||||
|
*/
|
||||||
|
void release(unsigned releaseDelay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Process a cycle of the generator.
|
||||||
|
*/
|
||||||
|
void process(absl::Span<float> out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Impl;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
|
|
@ -1046,6 +1046,80 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Modulation: Flex EG (targets)
|
||||||
|
case hash("eg&_amplitude"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::amplitudeRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Amplitude, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_pan"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::panCCRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pan, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_width"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::widthCCRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Width, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_position"): // sfizz extension
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::positionCCRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Position, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_pitch"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::tuneCCRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Pitch, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_volume"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::volumeCCRange)) {
|
||||||
|
const ModKey source = ModKey::createNXYZ(ModId::Envelope, id, egNumber - 1);
|
||||||
|
const ModKey target = ModKey::createNXYZ(ModId::Volume, id);
|
||||||
|
getOrCreateConnection(source, target).sourceDepth = *value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Amplitude Envelope
|
// Amplitude Envelope
|
||||||
case hash("ampeg_attack"):
|
case hash("ampeg_attack"):
|
||||||
setValueFromOpcode(opcode, amplitudeEG.attack, Default::egTimeRange);
|
setValueFromOpcode(opcode, amplitudeEG.attack, Default::egTimeRange);
|
||||||
|
|
@ -1155,6 +1229,73 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// Flex envelopes
|
||||||
|
case hash("eg&_dynamic"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
auto& eg = flexEGs[egNumber - 1];
|
||||||
|
setValueFromOpcode(opcode, eg.dynamic, Default::flexEGDynamicRange);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_sustain"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
auto& eg = flexEGs[egNumber - 1];
|
||||||
|
setValueFromOpcode(opcode, eg.sustain, Default::flexEGSustainRange);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_time&"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
auto& eg = flexEGs[egNumber - 1];
|
||||||
|
const auto pointNumber = opcode.parameters[1];
|
||||||
|
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
||||||
|
return false;
|
||||||
|
setValueFromOpcode(opcode, eg.points[pointNumber].time, Default::flexEGPointTimeRange);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_level&"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
auto& eg = flexEGs[egNumber - 1];
|
||||||
|
const auto pointNumber = opcode.parameters[1];
|
||||||
|
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
||||||
|
return false;
|
||||||
|
setValueFromOpcode(opcode, eg.points[pointNumber].level, Default::flexEGPointLevelRange);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case hash("eg&_shape&"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
auto& eg = flexEGs[egNumber - 1];
|
||||||
|
const auto pointNumber = opcode.parameters[1];
|
||||||
|
if (!extendIfNecessary(eg.points, pointNumber + 1, Default::numFlexEGPoints))
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Default::flexEGPointShapeRange))
|
||||||
|
eg.points[pointNumber].setShape(*value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case hash("effect&"):
|
case hash("effect&"):
|
||||||
{
|
{
|
||||||
const auto effectNumber = opcode.parameters.back();
|
const auto effectNumber = opcode.parameters.back();
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@
|
||||||
#include "LeakDetector.h"
|
#include "LeakDetector.h"
|
||||||
#include "Defaults.h"
|
#include "Defaults.h"
|
||||||
#include "EGDescription.h"
|
#include "EGDescription.h"
|
||||||
|
#include "FlexEGDescription.h"
|
||||||
#include "EQDescription.h"
|
#include "EQDescription.h"
|
||||||
#include "FilterDescription.h"
|
#include "FilterDescription.h"
|
||||||
#include "LFODescription.h"
|
#include "LFODescription.h"
|
||||||
|
|
@ -379,6 +380,9 @@ struct Region {
|
||||||
EGDescription pitchEG;
|
EGDescription pitchEG;
|
||||||
EGDescription filterEG;
|
EGDescription filterEG;
|
||||||
|
|
||||||
|
// Envelopes
|
||||||
|
std::vector<FlexEGDescription> flexEGs;
|
||||||
|
|
||||||
// LFOs
|
// LFOs
|
||||||
std::vector<LFODescription> lfos;
|
std::vector<LFODescription> lfos;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "modulations/ModId.h"
|
#include "modulations/ModId.h"
|
||||||
#include "modulations/sources/Controller.h"
|
#include "modulations/sources/Controller.h"
|
||||||
#include "modulations/sources/LFO.h"
|
#include "modulations/sources/LFO.h"
|
||||||
|
#include "modulations/sources/FlexEnvelope.h"
|
||||||
#include "pugixml.hpp"
|
#include "pugixml.hpp"
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
|
|
@ -46,6 +47,7 @@ sfz::Synth::Synth(int numVoices)
|
||||||
// modulation sources
|
// modulation sources
|
||||||
genController.reset(new ControllerSource(resources));
|
genController.reset(new ControllerSource(resources));
|
||||||
genLFO.reset(new LFOSource(*this));
|
genLFO.reset(new LFOSource(*this));
|
||||||
|
genFlexEnvelope.reset(new FlexEnvelopeSource(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
sfz::Synth::~Synth()
|
sfz::Synth::~Synth()
|
||||||
|
|
@ -480,6 +482,9 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
size_t maxFilters { 0 };
|
size_t maxFilters { 0 };
|
||||||
size_t maxEQs { 0 };
|
size_t maxEQs { 0 };
|
||||||
size_t maxLFOs { 0 };
|
size_t maxLFOs { 0 };
|
||||||
|
size_t maxFlexEGs { 0 };
|
||||||
|
|
||||||
|
FlexEGs::clearUnusedCurves();
|
||||||
|
|
||||||
while (currentRegionIndex < currentRegionCount) {
|
while (currentRegionIndex < currentRegionCount) {
|
||||||
auto region = regions[currentRegionIndex].get();
|
auto region = regions[currentRegionIndex].get();
|
||||||
|
|
@ -595,6 +600,7 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
maxFilters = max(maxFilters, region->filters.size());
|
maxFilters = max(maxFilters, region->filters.size());
|
||||||
maxEQs = max(maxEQs, region->equalizers.size());
|
maxEQs = max(maxEQs, region->equalizers.size());
|
||||||
maxLFOs = max(maxLFOs, region->lfos.size());
|
maxLFOs = max(maxLFOs, region->lfos.size());
|
||||||
|
maxFlexEGs = max(maxFlexEGs, region->flexEGs.size());
|
||||||
|
|
||||||
++currentRegionIndex;
|
++currentRegionIndex;
|
||||||
}
|
}
|
||||||
|
|
@ -605,6 +611,7 @@ void sfz::Synth::finalizeSfzLoad()
|
||||||
settingsPerVoice.maxFilters = maxFilters;
|
settingsPerVoice.maxFilters = maxFilters;
|
||||||
settingsPerVoice.maxEQs = maxEQs;
|
settingsPerVoice.maxEQs = maxEQs;
|
||||||
settingsPerVoice.maxLFOs = maxLFOs;
|
settingsPerVoice.maxLFOs = maxLFOs;
|
||||||
|
settingsPerVoice.maxFlexEGs = maxFlexEGs;
|
||||||
|
|
||||||
applySettingsPerVoice();
|
applySettingsPerVoice();
|
||||||
|
|
||||||
|
|
@ -1473,6 +1480,7 @@ void sfz::Synth::applySettingsPerVoice()
|
||||||
voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters);
|
voice->setMaxFiltersPerVoice(settingsPerVoice.maxFilters);
|
||||||
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
|
voice->setMaxEQsPerVoice(settingsPerVoice.maxEQs);
|
||||||
voice->setMaxLFOsPerVoice(settingsPerVoice.maxLFOs);
|
voice->setMaxLFOsPerVoice(settingsPerVoice.maxLFOs);
|
||||||
|
voice->setMaxFlexEGsPerVoice(settingsPerVoice.maxFlexEGs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1491,6 +1499,9 @@ void sfz::Synth::setupModMatrix()
|
||||||
case ModId::LFO:
|
case ModId::LFO:
|
||||||
gen = genLFO.get();
|
gen = genLFO.get();
|
||||||
break;
|
break;
|
||||||
|
case ModId::Envelope:
|
||||||
|
gen = genFlexEnvelope.get();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
DBG("[sfizz] Have unknown type of source generator");
|
DBG("[sfizz] Have unknown type of source generator");
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
class ControllerSource;
|
class ControllerSource;
|
||||||
class LFOSource;
|
class LFOSource;
|
||||||
|
class FlexEnvelopeSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This class is the core of the sfizz library. In C++ it is the main point
|
* @brief This class is the core of the sfizz library. In C++ it is the main point
|
||||||
|
|
@ -897,12 +898,14 @@ private:
|
||||||
// Modulation source generators
|
// Modulation source generators
|
||||||
std::unique_ptr<ControllerSource> genController;
|
std::unique_ptr<ControllerSource> genController;
|
||||||
std::unique_ptr<LFOSource> genLFO;
|
std::unique_ptr<LFOSource> genLFO;
|
||||||
|
std::unique_ptr<FlexEnvelopeSource> genFlexEnvelope;
|
||||||
|
|
||||||
// Settings per voice
|
// Settings per voice
|
||||||
struct SettingsPerVoice {
|
struct SettingsPerVoice {
|
||||||
size_t maxFilters { 0 };
|
size_t maxFilters { 0 };
|
||||||
size_t maxEQs { 0 };
|
size_t maxEQs { 0 };
|
||||||
size_t maxLFOs { 0 };
|
size_t maxLFOs { 0 };
|
||||||
|
size_t maxFlexEGs { 0 };
|
||||||
};
|
};
|
||||||
SettingsPerVoice settingsPerVoice;
|
SettingsPerVoice settingsPerVoice;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
#include "Panning.h"
|
#include "Panning.h"
|
||||||
#include "SfzHelpers.h"
|
#include "SfzHelpers.h"
|
||||||
#include "LFO.h"
|
#include "LFO.h"
|
||||||
|
#include "FlexEnvelope.h"
|
||||||
#include "modulations/ModId.h"
|
#include "modulations/ModId.h"
|
||||||
#include "modulations/ModKey.h"
|
#include "modulations/ModKey.h"
|
||||||
#include "modulations/ModMatrix.h"
|
#include "modulations/ModMatrix.h"
|
||||||
|
|
@ -798,6 +799,17 @@ void sfz::Voice::setMaxLFOsPerVoice(size_t numLFOs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sfz::Voice::setMaxFlexEGsPerVoice(size_t numFlexEGs)
|
||||||
|
{
|
||||||
|
flexEGs.resize(numFlexEGs);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numFlexEGs; ++i) {
|
||||||
|
auto eg = absl::make_unique<FlexEnvelope>();
|
||||||
|
eg->setSampleRate(sampleRate);
|
||||||
|
flexEGs[i] = std::move(eg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void sfz::Voice::setupOscillatorUnison()
|
void sfz::Voice::setupOscillatorUnison()
|
||||||
{
|
{
|
||||||
int m = region->oscillatorMulti;
|
int m = region->oscillatorMulti;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
namespace sfz {
|
namespace sfz {
|
||||||
enum InterpolatorModel : int;
|
enum InterpolatorModel : int;
|
||||||
class LFO;
|
class LFO;
|
||||||
|
class FlexEnvelope;
|
||||||
/**
|
/**
|
||||||
* @brief The SFZ voice are the polyphony holders. They get activated by the synth
|
* @brief The SFZ voice are the polyphony holders. They get activated by the synth
|
||||||
* and tasked to play a given region until the end, stopping on note-offs, off-groups
|
* and tasked to play a given region until the end, stopping on note-offs, off-groups
|
||||||
|
|
@ -263,6 +264,12 @@ public:
|
||||||
* @param index
|
* @param index
|
||||||
*/
|
*/
|
||||||
LFO* getLFO(size_t index) { return lfos[index].get(); }
|
LFO* getLFO(size_t index) { return lfos[index].get(); }
|
||||||
|
/**
|
||||||
|
* @brief Get the Flex EG designated by the given index
|
||||||
|
*
|
||||||
|
* @param index
|
||||||
|
*/
|
||||||
|
FlexEnvelope* getFlexEG(size_t index) { return flexEGs[index].get(); }
|
||||||
/**
|
/**
|
||||||
* @brief Set the max number of filters per voice
|
* @brief Set the max number of filters per voice
|
||||||
*
|
*
|
||||||
|
|
@ -281,6 +288,12 @@ public:
|
||||||
* @param numLFOs
|
* @param numLFOs
|
||||||
*/
|
*/
|
||||||
void setMaxLFOsPerVoice(size_t numLFOs);
|
void setMaxLFOsPerVoice(size_t numLFOs);
|
||||||
|
/**
|
||||||
|
* @brief Set the max number of Flex EGs per voice
|
||||||
|
*
|
||||||
|
* @param numFlexEGs
|
||||||
|
*/
|
||||||
|
void setMaxFlexEGsPerVoice(size_t numFlexEGs);
|
||||||
/**
|
/**
|
||||||
* @brief Release the voice after a given delay
|
* @brief Release the voice after a given delay
|
||||||
*
|
*
|
||||||
|
|
@ -444,6 +457,7 @@ private:
|
||||||
std::vector<FilterHolderPtr> filters;
|
std::vector<FilterHolderPtr> filters;
|
||||||
std::vector<EQHolderPtr> equalizers;
|
std::vector<EQHolderPtr> equalizers;
|
||||||
std::vector<std::unique_ptr<LFO>> lfos;
|
std::vector<std::unique_ptr<LFO>> lfos;
|
||||||
|
std::vector<std::unique_ptr<FlexEnvelope>> flexEGs;
|
||||||
|
|
||||||
ADSREnvelope<float> egEnvelope;
|
ADSREnvelope<float> egEnvelope;
|
||||||
float bendStepFactor { centsFactor(1) };
|
float bendStepFactor { centsFactor(1) };
|
||||||
|
|
|
||||||
86
src/sfizz/modulations/sources/FlexEnvelope.cpp
Normal file
86
src/sfizz/modulations/sources/FlexEnvelope.cpp
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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 "FlexEnvelope.h"
|
||||||
|
#include "../../FlexEnvelope.h"
|
||||||
|
#include "../../Synth.h"
|
||||||
|
#include "../../Voice.h"
|
||||||
|
#include "../../SIMDHelpers.h"
|
||||||
|
#include "../../Config.h"
|
||||||
|
#include "../../Debug.h"
|
||||||
|
|
||||||
|
namespace sfz {
|
||||||
|
|
||||||
|
FlexEnvelopeSource::FlexEnvelopeSource(Synth &synth)
|
||||||
|
: synth_(&synth)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
|
||||||
|
{
|
||||||
|
Synth& synth = *synth_;
|
||||||
|
unsigned egIndex = sourceKey.parameters().N;
|
||||||
|
|
||||||
|
Voice* voice = synth.getVoiceById(voiceId);
|
||||||
|
if (!voice) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Region* region = voice->getRegion();
|
||||||
|
if (egIndex >= region->flexEGs.size()) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexEnvelope* eg = voice->getFlexEG(egIndex);
|
||||||
|
eg->configure(®ion->flexEGs[egIndex]);
|
||||||
|
eg->start(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelopeSource::release(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
|
||||||
|
{
|
||||||
|
Synth& synth = *synth_;
|
||||||
|
unsigned egIndex = sourceKey.parameters().N;
|
||||||
|
|
||||||
|
Voice* voice = synth.getVoiceById(voiceId);
|
||||||
|
if (!voice) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Region* region = voice->getRegion();
|
||||||
|
if (egIndex >= region->flexEGs.size()) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexEnvelope* eg = voice->getFlexEG(egIndex);
|
||||||
|
eg->release(delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlexEnvelopeSource::generate(const ModKey& sourceKey, NumericId<Voice> voiceId, absl::Span<float> buffer)
|
||||||
|
{
|
||||||
|
Synth& synth = *synth_;
|
||||||
|
unsigned egIndex = sourceKey.parameters().N;
|
||||||
|
|
||||||
|
Voice* voice = synth.getVoiceById(voiceId);
|
||||||
|
if (!voice) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Region* region = voice->getRegion();
|
||||||
|
if (egIndex >= region->flexEGs.size()) {
|
||||||
|
ASSERTFALSE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlexEnvelope* eg = voice->getFlexEG(egIndex);
|
||||||
|
eg->process(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sfz
|
||||||
24
src/sfizz/modulations/sources/FlexEnvelope.h
Normal file
24
src/sfizz/modulations/sources/FlexEnvelope.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 FlexEnvelopeSource : public ModGenerator {
|
||||||
|
public:
|
||||||
|
explicit FlexEnvelopeSource(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