Implement egN_ampeg
This commit is contained in:
parent
3412ead0ac
commit
c30cad2534
7 changed files with 119 additions and 19 deletions
|
|
@ -34,6 +34,8 @@ struct FlexEGDescription {
|
||||||
int dynamic { Default::flexEGDynamic }; // whether parameters can be modulated while EG runs
|
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)
|
int sustain { Default::flexEGSustain }; // index of the sustain point (default to 0 in ARIA)
|
||||||
std::vector<FlexEGPoint> points;
|
std::vector<FlexEGPoint> points;
|
||||||
|
// ARIA
|
||||||
|
bool ampeg = false; // replaces the SFZv1 AmpEG (lowest with this bit wins)
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,25 @@ void FlexEnvelope::release(unsigned releaseDelay)
|
||||||
impl.currentFramesUntilRelease_ = releaseDelay;
|
impl.currentFramesUntilRelease_ = releaseDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned FlexEnvelope::getRemainingDelay() const noexcept
|
||||||
|
{
|
||||||
|
const Impl& impl = *impl_;
|
||||||
|
return static_cast<unsigned>(impl.delayFramesLeft_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlexEnvelope::isReleased() const noexcept
|
||||||
|
{
|
||||||
|
const Impl& impl = *impl_;
|
||||||
|
return impl.isReleased_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FlexEnvelope::isFinished() const noexcept
|
||||||
|
{
|
||||||
|
const Impl& impl = *impl_;
|
||||||
|
const FlexEGDescription& desc = *impl.desc_;
|
||||||
|
return impl.currentStageNumber_ >= desc.points.size();
|
||||||
|
}
|
||||||
|
|
||||||
void FlexEnvelope::process(absl::Span<float> out)
|
void FlexEnvelope::process(absl::Span<float> out)
|
||||||
{
|
{
|
||||||
Impl& impl = *impl_;
|
Impl& impl = *impl_;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,21 @@ public:
|
||||||
*/
|
*/
|
||||||
void release(unsigned releaseDelay);
|
void release(unsigned releaseDelay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
Get the remaining delay samples
|
||||||
|
*/
|
||||||
|
unsigned getRemainingDelay() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Is the envelope released?
|
||||||
|
*/
|
||||||
|
bool isReleased() const noexcept;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Is the envelope finished?
|
||||||
|
*/
|
||||||
|
bool isFinished() const noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Process a cycle of the generator.
|
Process a cycle of the generator.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1162,6 +1162,28 @@ bool sfz::Region::parseOpcode(const Opcode& rawOpcode)
|
||||||
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthModRange);
|
LFO_EG_filter_EQ_target(ModId::Envelope, ModId::EqBandwidth, Default::eqBandwidthModRange);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case hash("eg&_ampeg"):
|
||||||
|
{
|
||||||
|
const auto egNumber = opcode.parameters.front();
|
||||||
|
if (egNumber == 0)
|
||||||
|
return false;
|
||||||
|
if (!extendIfNecessary(flexEGs, egNumber, Default::numFlexEGs))
|
||||||
|
return false;
|
||||||
|
if (auto value = readOpcode(opcode.value, Range<int> { 0, 1 })) {
|
||||||
|
FlexEGDescription& desc = flexEGs[egNumber - 1];
|
||||||
|
bool ampeg = *value != 0;
|
||||||
|
if (desc.ampeg != ampeg) {
|
||||||
|
desc.ampeg = ampeg;
|
||||||
|
flexAmpEG = absl::nullopt;
|
||||||
|
for (size_t i = 0, n = flexEGs.size(); i < n && !flexAmpEG; ++i) {
|
||||||
|
if (flexEGs[i].ampeg)
|
||||||
|
flexAmpEG = static_cast<uint8_t>(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Amplitude Envelope
|
// Amplitude Envelope
|
||||||
case hash("ampeg_attack"):
|
case hash("ampeg_attack"):
|
||||||
case hash("ampeg_decay"):
|
case hash("ampeg_decay"):
|
||||||
|
|
@ -1907,7 +1929,7 @@ float sfz::Region::getBendInCents(float bend) const noexcept
|
||||||
return bend > 0.0f ? bend * static_cast<float>(bendUp) : -bend * static_cast<float>(bendDown);
|
return bend > 0.0f ? bend * static_cast<float>(bendUp) : -bend * static_cast<float>(bendDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source, const ModKey& target)
|
sfz::Region::Connection* sfz::Region::getConnection(const ModKey& source, const ModKey& target)
|
||||||
{
|
{
|
||||||
auto pred = [&source, &target](const Connection& c)
|
auto pred = [&source, &target](const Connection& c)
|
||||||
{
|
{
|
||||||
|
|
@ -1915,8 +1937,13 @@ sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = std::find_if(connections.begin(), connections.end(), pred);
|
auto it = std::find_if(connections.begin(), connections.end(), pred);
|
||||||
if (it != connections.end())
|
return (it == connections.end()) ? nullptr : &*it;
|
||||||
return *it;
|
}
|
||||||
|
|
||||||
|
sfz::Region::Connection& sfz::Region::getOrCreateConnection(const ModKey& source, const ModKey& target)
|
||||||
|
{
|
||||||
|
if (Connection* c = getConnection(source, target))
|
||||||
|
return *c;
|
||||||
|
|
||||||
sfz::Region::Connection c;
|
sfz::Region::Connection c;
|
||||||
c.source = source;
|
c.source = source;
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,7 @@ struct Region {
|
||||||
|
|
||||||
// Envelopes
|
// Envelopes
|
||||||
std::vector<FlexEGDescription> flexEGs;
|
std::vector<FlexEGDescription> flexEGs;
|
||||||
|
absl::optional<uint8_t> flexAmpEG; // egN_ampeg
|
||||||
|
|
||||||
// LFOs
|
// LFOs
|
||||||
std::vector<LFODescription> lfos;
|
std::vector<LFODescription> lfos;
|
||||||
|
|
@ -445,6 +446,7 @@ struct Region {
|
||||||
float velToDepth = 0.0f;
|
float velToDepth = 0.0f;
|
||||||
};
|
};
|
||||||
std::vector<Connection> connections;
|
std::vector<Connection> connections;
|
||||||
|
Connection* getConnection(const ModKey& source, const ModKey& target);
|
||||||
Connection& getOrCreateConnection(const ModKey& source, const ModKey& target);
|
Connection& getOrCreateConnection(const ModKey& source, const ModKey& target);
|
||||||
|
|
||||||
// Parent
|
// Parent
|
||||||
|
|
|
||||||
|
|
@ -159,11 +159,6 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
ModKey::createCC(10, 1, defaultSmoothness, 100, 0),
|
ModKey::createCC(10, 1, defaultSmoothness, 100, 0),
|
||||||
ModKey::createNXYZ(ModId::Pan, lastRegion->id)).sourceDepth = 1.0f;
|
ModKey::createNXYZ(ModId::Pan, lastRegion->id)).sourceDepth = 1.0f;
|
||||||
|
|
||||||
// Create the amplitude envelope
|
|
||||||
lastRegion->getOrCreateConnection(
|
|
||||||
ModKey::createNXYZ(ModId::AmpEG, lastRegion->id),
|
|
||||||
ModKey::createNXYZ(ModId::Amplitude, lastRegion->id)).sourceDepth = 100.0f;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
auto parseOpcodes = [&](const std::vector<Opcode>& opcodes) {
|
auto parseOpcodes = [&](const std::vector<Opcode>& opcodes) {
|
||||||
for (auto& opcode : opcodes) {
|
for (auto& opcode : opcodes) {
|
||||||
|
|
@ -182,6 +177,19 @@ void sfz::Synth::buildRegion(const std::vector<Opcode>& regionOpcodes)
|
||||||
parseOpcodes(groupOpcodes);
|
parseOpcodes(groupOpcodes);
|
||||||
parseOpcodes(regionOpcodes);
|
parseOpcodes(regionOpcodes);
|
||||||
|
|
||||||
|
// Create the amplitude envelope
|
||||||
|
if (!lastRegion->flexAmpEG) {
|
||||||
|
lastRegion->getOrCreateConnection(
|
||||||
|
ModKey::createNXYZ(ModId::AmpEG, lastRegion->id),
|
||||||
|
ModKey::createNXYZ(ModId::Amplitude, lastRegion->id)).sourceDepth = 100.0f;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ModKey source = ModKey::createNXYZ(ModId::Envelope, lastRegion->id, *lastRegion->flexAmpEG);
|
||||||
|
ModKey target = ModKey::createNXYZ(ModId::Amplitude, lastRegion->id);
|
||||||
|
if (!lastRegion->getConnection(source, target))
|
||||||
|
lastRegion->getOrCreateConnection(source, target).sourceDepth = 100.0f;
|
||||||
|
}
|
||||||
|
|
||||||
if (octaveOffset != 0 || noteOffset != 0)
|
if (octaveOffset != 0 || noteOffset != 0)
|
||||||
lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset);
|
lastRegion->offsetAllKeys(octaveOffset * 12 + noteOffset);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -151,8 +151,13 @@ void sfz::Voice::release(int delay) noexcept
|
||||||
if (state != State::playing)
|
if (state != State::playing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (egAmplitude.getRemainingDelay() > delay) {
|
if (!region->flexAmpEG) {
|
||||||
switchState(State::cleanMeUp);
|
if (egAmplitude.getRemainingDelay() > delay)
|
||||||
|
switchState(State::cleanMeUp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (flexEGs[*region->flexAmpEG]->getRemainingDelay() > static_cast<unsigned>(delay))
|
||||||
|
switchState(State::cleanMeUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
resources.modMatrix.releaseVoice(id, region->getId(), delay);
|
resources.modMatrix.releaseVoice(id, region->getId(), delay);
|
||||||
|
|
@ -160,10 +165,15 @@ void sfz::Voice::release(int delay) noexcept
|
||||||
|
|
||||||
void sfz::Voice::off(int delay) noexcept
|
void sfz::Voice::off(int delay) noexcept
|
||||||
{
|
{
|
||||||
if (region->offMode == SfzOffMode::fast) {
|
if (!region->flexAmpEG) {
|
||||||
egAmplitude.setReleaseTime( Default::offTime );
|
if (region->offMode == SfzOffMode::fast) {
|
||||||
} else if (region->offMode == SfzOffMode::time) {
|
egAmplitude.setReleaseTime( Default::offTime );
|
||||||
egAmplitude.setReleaseTime(region->offTime);
|
} else if (region->offMode == SfzOffMode::time) {
|
||||||
|
egAmplitude.setReleaseTime(region->offTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO(jpc): Flex AmpEG
|
||||||
}
|
}
|
||||||
|
|
||||||
release(delay);
|
release(delay);
|
||||||
|
|
@ -283,8 +293,14 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
|
||||||
panStageMono(buffer);
|
panStageMono(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!egAmplitude.isSmoothing())
|
if (!region->flexAmpEG) {
|
||||||
switchState(State::cleanMeUp);
|
if (!egAmplitude.isSmoothing())
|
||||||
|
switchState(State::cleanMeUp);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (flexEGs[*region->flexAmpEG]->isFinished())
|
||||||
|
switchState(State::cleanMeUp);
|
||||||
|
}
|
||||||
|
|
||||||
powerFollower.process(buffer);
|
powerFollower.process(buffer);
|
||||||
|
|
||||||
|
|
@ -569,8 +585,14 @@ void sfz::Voice::fillWithData(AudioSpan<float> buffer) noexcept
|
||||||
<< " for sample " << region->sampleId);
|
<< " for sample " << region->sampleId);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
egAmplitude.setReleaseTime(0.0f);
|
if (!region->flexAmpEG) {
|
||||||
egAmplitude.startRelease(i);
|
egAmplitude.setReleaseTime(0.0f);
|
||||||
|
egAmplitude.startRelease(i);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// TODO(jpc): Flex AmpEG
|
||||||
|
flexEGs[*region->flexAmpEG]->release(i);
|
||||||
|
}
|
||||||
fill<int>(indices->subspan(i), sampleEnd);
|
fill<int>(indices->subspan(i), sampleEnd);
|
||||||
fill<float>(coeffs->subspan(i), 1.0f);
|
fill<float>(coeffs->subspan(i), 1.0f);
|
||||||
break;
|
break;
|
||||||
|
|
@ -850,7 +872,12 @@ float sfz::Voice::getAveragePower() const noexcept
|
||||||
|
|
||||||
bool sfz::Voice::releasedOrFree() const noexcept
|
bool sfz::Voice::releasedOrFree() const noexcept
|
||||||
{
|
{
|
||||||
return state != State::playing || egAmplitude.isReleased();
|
if (state != State::playing)
|
||||||
|
return true;
|
||||||
|
if (!region->flexAmpEG)
|
||||||
|
return egAmplitude.isReleased();
|
||||||
|
else
|
||||||
|
return flexEGs[*region->flexAmpEG]->isReleased();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t sfz::Voice::getSourcePosition() const noexcept
|
uint32_t sfz::Voice::getSourcePosition() const noexcept
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue