commit
6b1a7cd259
5 changed files with 115 additions and 14 deletions
|
|
@ -57,6 +57,7 @@ struct FlexEnvelope::Impl {
|
||||||
void process(absl::Span<float> out);
|
void process(absl::Span<float> out);
|
||||||
bool advanceToStage(unsigned stageNumber);
|
bool advanceToStage(unsigned stageNumber);
|
||||||
bool advanceToNextStage();
|
bool advanceToNextStage();
|
||||||
|
void updateCurrentTimeAndLevel();
|
||||||
};
|
};
|
||||||
|
|
||||||
FlexEnvelope::FlexEnvelope(Resources &resources)
|
FlexEnvelope::FlexEnvelope(Resources &resources)
|
||||||
|
|
@ -97,18 +98,8 @@ void FlexEnvelope::start(unsigned triggerDelay)
|
||||||
const FlexEGDescription& desc = *impl.desc_;
|
const FlexEGDescription& desc = *impl.desc_;
|
||||||
|
|
||||||
impl.delayFramesLeft_ = triggerDelay;
|
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.currentFramesUntilRelease_ = absl::nullopt;
|
||||||
|
impl.advanceToStage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlexEnvelope::setFreeRunning(bool freeRunning)
|
void FlexEnvelope::setFreeRunning(bool freeRunning)
|
||||||
|
|
@ -172,6 +163,7 @@ void FlexEnvelope::Impl::process(absl::Span<float> out)
|
||||||
const FlexEGDescription& desc = *desc_;
|
const FlexEGDescription& desc = *desc_;
|
||||||
size_t numFrames = out.size();
|
size_t numFrames = out.size();
|
||||||
const float samplePeriod = samplePeriod_;
|
const float samplePeriod = samplePeriod_;
|
||||||
|
|
||||||
// Skip the initial delay, for frame-accurate trigger
|
// Skip the initial delay, for frame-accurate trigger
|
||||||
size_t skipFrames = std::min(numFrames, delayFramesLeft_);
|
size_t skipFrames = std::min(numFrames, delayFramesLeft_);
|
||||||
if (skipFrames > 0) {
|
if (skipFrames > 0) {
|
||||||
|
|
@ -266,12 +258,11 @@ bool FlexEnvelope::Impl::advanceToStage(unsigned stageNumber)
|
||||||
|
|
||||||
const FlexEGPoint& point = desc.points[stageNumber];
|
const FlexEGPoint& point = desc.points[stageNumber];
|
||||||
stageSourceLevel_ = currentLevel_;
|
stageSourceLevel_ = currentLevel_;
|
||||||
stageTargetLevel_ = point.getLevel(midiState);
|
currentTime_ = 0.0f;
|
||||||
stageTime_ = point.getTime(midiState);
|
updateCurrentTimeAndLevel();
|
||||||
stageSustained_ = int(stageNumber) == desc.sustain;
|
stageSustained_ = int(stageNumber) == desc.sustain;
|
||||||
stageCurve_ = &point.curve();
|
stageCurve_ = &point.curve();
|
||||||
|
|
||||||
currentTime_ = 0;
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -280,4 +271,16 @@ bool FlexEnvelope::Impl::advanceToNextStage()
|
||||||
return advanceToStage(currentStageNumber_ + 1);
|
return advanceToStage(currentStageNumber_ + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlexEnvelope::Impl::updateCurrentTimeAndLevel()
|
||||||
|
{
|
||||||
|
const FlexEGDescription& desc = *desc_;
|
||||||
|
if (currentStageNumber_ >= desc.points.size())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const FlexEGPoint& point = desc.points[currentStageNumber_];
|
||||||
|
const MidiState& midiState = resources_->getMidiState();
|
||||||
|
stageTargetLevel_ = point.getLevel(midiState);
|
||||||
|
stageTime_ = point.getTime(midiState);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sfz
|
} // namespace sfz
|
||||||
|
|
|
||||||
|
|
@ -2045,6 +2045,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, co
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccHold);
|
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccHold);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccStart);
|
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccStart);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccSustain);
|
collectUsedCCsFromCCMap(usedCCs, region.amplitudeEG.ccSustain);
|
||||||
|
|
||||||
if (region.pitchEG) {
|
if (region.pitchEG) {
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccAttack);
|
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccAttack);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccRelease);
|
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccRelease);
|
||||||
|
|
@ -2054,6 +2055,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, co
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccStart);
|
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccStart);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccSustain);
|
collectUsedCCsFromCCMap(usedCCs, region.pitchEG->ccSustain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.filterEG) {
|
if (region.filterEG) {
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccAttack);
|
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccAttack);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccRelease);
|
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccRelease);
|
||||||
|
|
@ -2063,6 +2065,7 @@ void Synth::Impl::collectUsedCCsFromRegion(BitArray<config::numCCs>& usedCCs, co
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccStart);
|
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccStart);
|
||||||
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccSustain);
|
collectUsedCCsFromCCMap(usedCCs, region.filterEG->ccSustain);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const LFODescription& lfo : region.lfos) {
|
for (const LFODescription& lfo : region.lfos) {
|
||||||
collectUsedCCsFromCCMap(usedCCs, lfo.phaseCC);
|
collectUsedCCsFromCCMap(usedCCs, lfo.phaseCC);
|
||||||
collectUsedCCsFromCCMap(usedCCs, lfo.delayCC);
|
collectUsedCCsFromCCMap(usedCCs, lfo.delayCC);
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,16 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
||||||
break; \
|
break; \
|
||||||
const auto& lfo = region.lfos[idx];
|
const auto& lfo = region.lfos[idx];
|
||||||
|
|
||||||
|
#define GET_EG_OR_BREAK(idx) \
|
||||||
|
if (idx >= region.flexEGs.size()) \
|
||||||
|
break; \
|
||||||
|
auto& eg = region.flexEGs[idx];
|
||||||
|
|
||||||
|
#define GET_EG_POINT_OR_BREAK(idx) \
|
||||||
|
if (idx >= eg.points.size()) \
|
||||||
|
break; \
|
||||||
|
auto& point = eg.points[idx];
|
||||||
|
|
||||||
MATCH("/hello", "") {
|
MATCH("/hello", "") {
|
||||||
client.receive(delay, "/hello", "", nullptr);
|
client.receive(delay, "/hello", "", nullptr);
|
||||||
} break;
|
} break;
|
||||||
|
|
@ -1352,10 +1362,44 @@ void sfz::Synth::dispatchMessage(Client& client, int delay, const char* path, co
|
||||||
client.receive<'i'>(delay, path, static_cast<int32_t>(lfo.sub[0].wave));
|
client.receive<'i'>(delay, path, static_cast<int32_t>(lfo.sub[0].wave));
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
|
MATCH("/region&/eg&/point&/time", "") {
|
||||||
|
GET_REGION_OR_BREAK(indices[0])
|
||||||
|
GET_EG_OR_BREAK(indices[1])
|
||||||
|
GET_EG_POINT_OR_BREAK(indices[2] + 1)
|
||||||
|
|
||||||
|
client.receive<'f'>(delay, path, point.time);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
MATCH("/region&/eg&/point&/time_cc&", "") {
|
||||||
|
GET_REGION_OR_BREAK(indices[0])
|
||||||
|
GET_EG_OR_BREAK(indices[1])
|
||||||
|
GET_EG_POINT_OR_BREAK(indices[2] + 1)
|
||||||
|
|
||||||
|
client.receive<'f'>(delay, path, point.ccTime.getWithDefault(indices[3]));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
MATCH("/region&/eg&/point&/level", "") {
|
||||||
|
GET_REGION_OR_BREAK(indices[0])
|
||||||
|
GET_EG_OR_BREAK(indices[1])
|
||||||
|
GET_EG_POINT_OR_BREAK(indices[2] + 1)
|
||||||
|
|
||||||
|
client.receive<'f'>(delay, path, point.level);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
MATCH("/region&/eg&/point&/level_cc&", "") {
|
||||||
|
GET_REGION_OR_BREAK(indices[0])
|
||||||
|
GET_EG_OR_BREAK(indices[1])
|
||||||
|
GET_EG_POINT_OR_BREAK(indices[2] + 1)
|
||||||
|
|
||||||
|
client.receive<'f'>(delay, path, point.ccLevel.getWithDefault(indices[3]));
|
||||||
|
} break;
|
||||||
|
|
||||||
#undef GET_REGION_OR_BREAK
|
#undef GET_REGION_OR_BREAK
|
||||||
#undef GET_FILTER_OR_BREAK
|
#undef GET_FILTER_OR_BREAK
|
||||||
#undef GET_EQ_OR_BREAK
|
#undef GET_EQ_OR_BREAK
|
||||||
#undef GET_LFO_OR_BREAK
|
#undef GET_LFO_OR_BREAK
|
||||||
|
#undef GET_EG_OR_BREAK
|
||||||
|
#undef GET_EG_POINT_OR_BREAK
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Setting values
|
// Setting values
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ namespace sfz {
|
||||||
FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager)
|
FlexEnvelopeSource::FlexEnvelopeSource(VoiceManager& manager)
|
||||||
: voiceManager_(manager)
|
: voiceManager_(manager)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
|
void FlexEnvelopeSource::init(const ModKey& sourceKey, NumericId<Voice> voiceId, unsigned delay)
|
||||||
|
|
|
||||||
|
|
@ -3251,3 +3251,53 @@ TEST_CASE("[Values] EQ value bounds")
|
||||||
REQUIRE(messageList == expected);
|
REQUIRE(messageList == expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Values] Flex EGs")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
|
||||||
|
<region> sample=kick.wav eg1_time1=0.1 eg1_level1=0.5 eg1_time2=0.4 eg1_level2=2 eg2_time1=4 eg2_level1=0.1
|
||||||
|
)");
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/time", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/level", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point1/time", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point1/level", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg1/point0/time", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg1/point0/level", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/region0/eg0/point0/time,f : { 0.1 }",
|
||||||
|
"/region0/eg0/point0/level,f : { 0.5 }",
|
||||||
|
"/region0/eg0/point1/time,f : { 0.4 }",
|
||||||
|
"/region0/eg0/point1/level,f : { 2 }",
|
||||||
|
"/region0/eg1/point0/time,f : { 4 }",
|
||||||
|
"/region0/eg1/point0/level,f : { 0.1 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("[Values] Flex EGs CC")
|
||||||
|
{
|
||||||
|
Synth synth;
|
||||||
|
std::vector<std::string> messageList;
|
||||||
|
Client client(&messageList);
|
||||||
|
client.setReceiveCallback(&simpleMessageReceiver);
|
||||||
|
|
||||||
|
synth.loadSfzString(fs::current_path() / "tests/TestFiles/value_tests.sfz", R"(
|
||||||
|
<region> sample=kick.wav eg1_time1_cc2=0.1 eg1_level1_oncc3=0.5
|
||||||
|
)");
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc2", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/time_cc4", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc3", "", nullptr);
|
||||||
|
synth.dispatchMessage(client, 0, "/region0/eg0/point0/level_cc12", "", nullptr);
|
||||||
|
std::vector<std::string> expected {
|
||||||
|
"/region0/eg0/point0/time_cc2,f : { 0.1 }",
|
||||||
|
"/region0/eg0/point0/time_cc4,f : { 0 }",
|
||||||
|
"/region0/eg0/point0/level_cc3,f : { 0.5 }",
|
||||||
|
"/region0/eg0/point0/level_cc12,f : { 0 }",
|
||||||
|
};
|
||||||
|
REQUIRE(messageList == expected);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue