Tentative replacement of the amplitude envelope

This commit is contained in:
Paul Ferrand 2020-03-29 16:25:58 +02:00
parent 612265ebb5
commit 42fa89ab5a
4 changed files with 37 additions and 17 deletions

View file

@ -299,7 +299,10 @@ bool sfz::Region::parseOpcode(const Opcode& opcode)
break; break;
case hash("amplitude_cc&"): // fallthrough case hash("amplitude_cc&"): // fallthrough
case hash("amplitude_oncc&"): case hash("amplitude_oncc&"):
setCCPairFromOpcode(opcode, amplitudeCC, Default::amplitudeRange); if (opcode.parameters.back() > config::numCCs)
return false;
if (auto value = readOpcode(opcode.value, Default::amplitudeRange))
amplitudeCC[opcode.parameters.back()] = *value;
break; break;
case hash("pan"): case hash("pan"):
setValueFromOpcode(opcode, pan, Default::panRange); setValueFromOpcode(opcode, pan, Default::panRange);

View file

@ -277,7 +277,7 @@ struct Region {
float width { Default::width }; // width float width { Default::width }; // width
float position { Default::position }; // position float position { Default::position }; // position
absl::optional<CCValuePair<float>> volumeCC; // volume_oncc absl::optional<CCValuePair<float>> volumeCC; // volume_oncc
absl::optional<CCValuePair<float>> amplitudeCC; // amplitude_oncc CCMap<float> amplitudeCC { Default::amplitude }; // amplitude_oncc
absl::optional<CCValuePair<float>> panCC; // pan_oncc absl::optional<CCValuePair<float>> panCC; // pan_oncc
absl::optional<CCValuePair<float>> widthCC; // width_oncc absl::optional<CCValuePair<float>> widthCC; // width_oncc
absl::optional<CCValuePair<float>> positionCC; // position_oncc absl::optional<CCValuePair<float>> positionCC; // position_oncc

View file

@ -85,11 +85,6 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, float value,
if (triggerType != TriggerType::CC) if (triggerType != TriggerType::CC)
baseGain *= region->getNoteGain(number, value); baseGain *= region->getNoteGain(number, value);
float gain { baseGain };
if (region->amplitudeCC)
gain += resources.midiState.getCCValue(region->amplitudeCC->cc) * normalizePercents(region->amplitudeCC->value);
amplitudeEnvelope.reset(Default::normalizedRange.clamp(gain));
float crossfadeGain { region->getCrossfadeGain() }; float crossfadeGain { region->getCrossfadeGain() };
crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain)); crossfadeEnvelope.reset(Default::normalizedRange.clamp(crossfadeGain));
@ -203,11 +198,6 @@ void sfz::Voice::registerCC(int delay, int ccNumber, float ccValue) noexcept
// TODO: this feels like a hack, revisit this along with the smoothed envelopes... // TODO: this feels like a hack, revisit this along with the smoothed envelopes...
delay = max(delay, minEnvelopeDelay); delay = max(delay, minEnvelopeDelay);
if (region->amplitudeCC && ccNumber == region->amplitudeCC->cc) {
const float newGain { baseGain + ccValue * normalizePercents(region->amplitudeCC->value) };
amplitudeEnvelope.registerEvent(delay, Default::normalizedRange.clamp(newGain));
}
if (region->volumeCC && ccNumber == region->volumeCC->cc) { if (region->volumeCC && ccNumber == region->volumeCC->cc) {
const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value }; const float newVolumedB { baseVolumedB + ccValue * region->volumeCC->value };
volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB))); volumeEnvelope.registerEvent(delay, db2mag(Default::volumeRange.clamp(newVolumedB)));
@ -311,6 +301,31 @@ void sfz::Voice::renderBlock(AudioSpan<float> buffer) noexcept
this->triggerDelay = absl::nullopt; this->triggerDelay = absl::nullopt;
} }
template<class T>
void getLinearEnvelope(const sfz::CCMap<T>& ccMods, const sfz::MidiState& state, absl::Span<float> output, absl::Span<float> temp)
{
ASSERT(output.size() <= temp.size());
sfz::fill<T>(output, T{0.0});
for (auto& mod : ccMods) {
const auto eventList = state.getEvents(mod.cc);
const auto modifier = static_cast<float>(mod.value);
ASSERT(eventList.size() > 0);
ASSERT(eventList[0].delay == 0);
auto lastValue = eventList[0].value;
auto lastDelay = eventList[0].delay;
for (unsigned i = 1; i < eventList.size(); ++ i) {
const auto event = eventList[i];
const auto length = event.delay - lastDelay;
const auto step = (event.value - lastValue) * modifier / length;
lastValue = sfz::linearRamp<T>(temp.subspan(lastDelay, length), lastValue, step);
lastDelay += length;
}
sfz::fill<T>(temp.subspan(lastDelay), lastValue * modifier);
sfz::add<T>(temp, output);
}
}
void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
{ {
const auto numSamples = buffer.getNumFrames(); const auto numSamples = buffer.getNumFrames();
@ -318,12 +333,13 @@ void sfz::Voice::processMono(AudioSpan<float> buffer) noexcept
auto rightBuffer = buffer.getSpan(1); auto rightBuffer = buffer.getSpan(1);
auto modulationSpan = tempSpan1.first(numSamples); auto modulationSpan = tempSpan1.first(numSamples);
auto tempSpan = tempSpan2.first(numSamples);
{ // Amplitude processing { // Amplitude processing
ScopedTiming logger { amplitudeDuration }; ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
amplitudeEnvelope.getBlock(modulationSpan); getLinearEnvelope(region->amplitudeCC, resources.midiState, modulationSpan, tempSpan);
applyGain<float>(modulationSpan, leftBuffer); applyGain<float>(modulationSpan, leftBuffer);
// Crossfade envelope // Crossfade envelope
@ -369,6 +385,7 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
{ {
const auto numSamples = buffer.getNumFrames(); const auto numSamples = buffer.getNumFrames();
auto modulationSpan = tempSpan1.first(numSamples); auto modulationSpan = tempSpan1.first(numSamples);
auto tempSpan = tempSpan2.first(numSamples);
auto leftBuffer = buffer.getSpan(0); auto leftBuffer = buffer.getSpan(0);
auto rightBuffer = buffer.getSpan(1); auto rightBuffer = buffer.getSpan(1);
@ -376,7 +393,7 @@ void sfz::Voice::processStereo(AudioSpan<float> buffer) noexcept
ScopedTiming logger { amplitudeDuration }; ScopedTiming logger { amplitudeDuration };
// Amplitude envelope // Amplitude envelope
amplitudeEnvelope.getBlock(modulationSpan); getLinearEnvelope(region->amplitudeCC, resources.midiState, modulationSpan, tempSpan);
buffer.applyGain(modulationSpan); buffer.applyGain(modulationSpan);
// Crossfade envelope // Crossfade envelope

View file

@ -309,9 +309,9 @@ TEST_CASE("[Files] wrong (overlapping) replacement for defines")
REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 ); REQUIRE( synth.getRegionView(0)->keyRange.getEnd() == 52 );
REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getStart() == 57 );
REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 ); REQUIRE( synth.getRegionView(1)->keyRange.getEnd() == 57 );
REQUIRE( synth.getRegionView(2)->amplitudeCC ); REQUIRE( !synth.getRegionView(2)->amplitudeCC.empty() );
REQUIRE( synth.getRegionView(2)->amplitudeCC->cc == 10 ); REQUIRE( synth.getRegionView(2)->amplitudeCC.contains(10) );
REQUIRE( synth.getRegionView(2)->amplitudeCC->value == 34.0f ); REQUIRE( synth.getRegionView(2)->amplitudeCC.getWithDefault(10) == 34.0f );
} }
TEST_CASE("[Files] Specific bug: relative path with backslashes") TEST_CASE("[Files] Specific bug: relative path with backslashes")