diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 3631e6c7..a458a837 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -70,7 +70,8 @@ namespace config { constexpr float powerFollowerReleaseTime { 200e-3f }; constexpr uint16_t numCCs { 512 }; constexpr int maxCurves { 256 }; - constexpr int chunkSize { 1024 }; + constexpr int fileChunkSize { 1024 }; + constexpr int processChunkSize { 16 }; constexpr unsigned int defaultAlignment { 16 }; constexpr int filtersInPool { maxVoices * 2 }; constexpr int excessFileFrames { 64 }; diff --git a/src/sfizz/FilePool.cpp b/src/sfizz/FilePool.cpp index 6bd4e35a..cce2941f 100644 --- a/src/sfizz/FilePool.cpp +++ b/src/sfizz/FilePool.cpp @@ -101,7 +101,7 @@ void streamFromFile(sfz::AudioReader& reader, sfz::FileAudioBuffer& output, std: { const auto numFrames = static_cast(reader.frames()); const auto numChannels = reader.channels(); - const auto chunkSize = static_cast(sfz::config::chunkSize); + const auto chunkSize = static_cast(sfz::config::fileChunkSize); output.reset(); output.addChannels(reader.channels()); diff --git a/src/sfizz/FlexEGDescription.cpp b/src/sfizz/FlexEGDescription.cpp index f92e20eb..98d99d21 100644 --- a/src/sfizz/FlexEGDescription.cpp +++ b/src/sfizz/FlexEGDescription.cpp @@ -86,19 +86,19 @@ void FlexEGs::clearUnusedCurves() } /// -float FlexEGPoint::getTime(const MidiState& state) const noexcept +float FlexEGPoint::getTime(const MidiState& state, int delay) const noexcept { float returnedValue { time }; for (const CCData& mod : ccTime) - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; return returnedValue; } -float FlexEGPoint::getLevel(const MidiState& state) const noexcept +float FlexEGPoint::getLevel(const MidiState& state, int delay) const noexcept { float returnedValue { level }; for (const CCData& mod : ccLevel) - returnedValue += state.getCCValue(mod.cc) * mod.data; + returnedValue += state.getCCValueAt(mod.cc, delay) * mod.data; return returnedValue; } diff --git a/src/sfizz/FlexEGDescription.h b/src/sfizz/FlexEGDescription.h index 4c816bef..8965746f 100644 --- a/src/sfizz/FlexEGDescription.h +++ b/src/sfizz/FlexEGDescription.h @@ -26,8 +26,8 @@ struct FlexEGPoint { CCMap ccTime; CCMap ccLevel; - float getTime(const MidiState& state) const noexcept; - float getLevel(const MidiState& state) const noexcept; + float getTime(const MidiState& state, int delay = 0) const noexcept; + float getLevel(const MidiState& state, int delay = 0) const noexcept; void setShape(float shape); float shape() const noexcept { return shape_; } diff --git a/src/sfizz/FlexEnvelope.cpp b/src/sfizz/FlexEnvelope.cpp index 3cbd4161..2d75dd65 100644 --- a/src/sfizz/FlexEnvelope.cpp +++ b/src/sfizz/FlexEnvelope.cpp @@ -57,7 +57,7 @@ struct FlexEnvelope::Impl { void process(absl::Span out); bool advanceToStage(unsigned stageNumber); bool advanceToNextStage(); - void updateCurrentTimeAndLevel(); + void updateCurrentTimeAndLevel(int delay = 0); }; FlexEnvelope::FlexEnvelope(Resources &resources) @@ -155,7 +155,19 @@ bool FlexEnvelope::isFinished() const noexcept void FlexEnvelope::process(absl::Span out) { Impl& impl = *impl_; - impl.process(out); + if (impl.desc_->dynamic) { + int processed = 0; + int remaining = static_cast(out.size()); + while(remaining > 0) { + impl.updateCurrentTimeAndLevel(processed); + int chunkSize = min(config::processChunkSize, remaining); + impl.process(out.subspan(processed, chunkSize)); + processed += chunkSize; + remaining -= chunkSize; + } + } else { + impl.process(out); + } } void FlexEnvelope::Impl::process(absl::Span out) @@ -271,7 +283,7 @@ bool FlexEnvelope::Impl::advanceToNextStage() return advanceToStage(currentStageNumber_ + 1); } -void FlexEnvelope::Impl::updateCurrentTimeAndLevel() +void FlexEnvelope::Impl::updateCurrentTimeAndLevel(int delay) { const FlexEGDescription& desc = *desc_; if (currentStageNumber_ >= desc.points.size()) @@ -279,8 +291,8 @@ void FlexEnvelope::Impl::updateCurrentTimeAndLevel() const FlexEGPoint& point = desc.points[currentStageNumber_]; const MidiState& midiState = resources_->getMidiState(); - stageTargetLevel_ = point.getLevel(midiState); - stageTime_ = point.getTime(midiState); + stageTargetLevel_ = point.getLevel(midiState, delay); + stageTime_ = point.getTime(midiState, delay); } } // namespace sfz diff --git a/src/sfizz/MidiState.cpp b/src/sfizz/MidiState.cpp index 4abaffb8..4d0d45eb 100644 --- a/src/sfizz/MidiState.cpp +++ b/src/sfizz/MidiState.cpp @@ -202,6 +202,17 @@ float sfz::MidiState::getCCValue(int ccNumber) const noexcept return ccEvents[ccNumber].back().value; } +float sfz::MidiState::getCCValueAt(int ccNumber, int delay) const noexcept +{ + ASSERT(ccNumber >= 0 && ccNumber < config::numCCs); + const auto ccEvent = absl::c_lower_bound( + ccEvents[ccNumber], delay, MidiEventDelayComparator {}); + if (ccEvent != ccEvents[ccNumber].end()) + return ccEvent->value; + else + return ccEvents[ccNumber].back().value; +} + void sfz::MidiState::reset() noexcept { for (auto& velocity: lastNoteVelocities) diff --git a/src/sfizz/MidiState.h b/src/sfizz/MidiState.h index 16197c2f..eaf947c6 100644 --- a/src/sfizz/MidiState.h +++ b/src/sfizz/MidiState.h @@ -167,13 +167,22 @@ public: bool isNotePressed(int noteNumber) const noexcept { return noteStates[noteNumber]; } /** - * @brief Get the CC value for CC number + * @brief Get the last CC value for CC number * * @param ccNumber * @return float */ float getCCValue(int ccNumber) const noexcept; + /** + * @brief Get the CC value for CC number + * + * @param ccNumber + * @param delay + * @return float + */ + float getCCValueAt(int ccNumber, int delay) const noexcept; + /** * @brief Reset the midi state (does not impact the last note on time) * diff --git a/src/sfizz/Oversampler.h b/src/sfizz/Oversampler.h index e109384c..745a4479 100644 --- a/src/sfizz/Oversampler.h +++ b/src/sfizz/Oversampler.h @@ -39,7 +39,7 @@ public: * @param factor * @param chunkSize */ - Oversampler(Oversampling factor = Oversampling::x1, size_t chunkSize = config::chunkSize); + Oversampler(Oversampling factor = Oversampling::x1, size_t chunkSize = config::fileChunkSize); /** * @brief Stream the oversampling of an input AudioBuffer into an output * one, possibly signaling the caller along the way of the number of