diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index e9818e39..22a18327 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -53,7 +53,9 @@ namespace config { constexpr uint8_t numCCs { 143 }; constexpr int chunkSize { 1024 }; constexpr float defaultAmpEGRelease { 0.02f }; - constexpr int defaultNumFilters { maxVoices * 2 }; + constexpr int filtersInPool { maxVoices * 2 }; + constexpr int filtersPerVoice { 2 }; + constexpr int eqsPerVoice { 3 }; /** Minimum interval in frames between recomputations of coefficients of the modulated filter. The lower, the more CPU resources are consumed. diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h index f7d910f7..cb1da2c9 100644 --- a/src/sfizz/FilterPool.h +++ b/src/sfizz/FilterPool.h @@ -77,7 +77,7 @@ public: * @param state the associated midi state * @param numFilters the number of inactive filters to hold in the pool */ - FilterPool(const MidiState& state, int numFilters = config::defaultNumFilters); + FilterPool(const MidiState& state, int numFilters = config::filtersInPool); /** * @brief Get a filter object to use in Voices * diff --git a/src/sfizz/Synth.cpp b/src/sfizz/Synth.cpp index d6cd7f7d..53761633 100644 --- a/src/sfizz/Synth.cpp +++ b/src/sfizz/Synth.cpp @@ -234,6 +234,9 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) ++lastRegion; }; + size_t maxFilters { 0 }; + size_t maxEQs { 0 }; + while (currentRegion < lastRegion.base()) { auto region = currentRegion->get(); @@ -316,6 +319,8 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) region->registerPitchWheel(0); region->registerAftertouch(0); region->registerTempo(2.0f); + maxFilters = max(maxFilters, region->filters.size()); + maxEQs = max(maxEQs, region->equalizers.size()); ++currentRegion; } @@ -324,6 +329,11 @@ bool sfz::Synth::loadSfzFile(const fs::path& file) regions.resize(remainingRegions); modificationTime = checkModificationTime(); + for (auto& voice: voices) { + voice->setMaxFiltersPerVoice(maxFilters); + voice->setMaxEQsPerVoice(maxEQs); + } + return parserReturned; } diff --git a/src/sfizz/Voice.cpp b/src/sfizz/Voice.cpp index 14aac783..e39faf7a 100644 --- a/src/sfizz/Voice.cpp +++ b/src/sfizz/Voice.cpp @@ -17,6 +17,8 @@ sfz::Voice::Voice(sfz::Resources& resources) : resources(resources) { + filters.reserve(config::filtersPerVoice); + equalizers.reserve(config::filtersPerVoice); } void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value, sfz::Voice::TriggerType triggerType) noexcept @@ -85,6 +87,18 @@ void sfz::Voice::startVoice(Region* region, int delay, int number, uint8_t value }); pitchBendEnvelope.reset(static_cast(resources.midiState.getPitchBend())); + // Check that we can handle the number of filters; filters should be cleared here + ASSERT((filters.capacity() - filters.size()) >= region->filters.size()); + ASSERT((equalizers.capacity() - equalizers.size()) >= region->equalizers.size()); + + for(auto& filter: region->filters) { + auto newFilter = resources.filterPool.getFilter(filter, number, value); + if (newFilter) + filters.push_back(newFilter); + } + + // TODO: Equalizers + sourcePosition = region->getOffset(); triggerDelay = delay; initialDelay = delay + static_cast(region->getDelay() * sampleRate); @@ -258,6 +272,12 @@ void sfz::Voice::renderBlock(AudioSpan buffer) noexcept else processMono(buffer); + const float* inputChannels[2] { buffer.getChannel(0), buffer.getChannel(1) }; + float* outputChannels[2] { buffer.getChannel(0), buffer.getChannel(1) }; + for (auto filter: filters) { + filter->process(inputChannels, outputChannels, buffer.getNumFrames()); + } + if (!egEnvelope.isSmoothing()) reset(); @@ -528,6 +548,8 @@ void sfz::Voice::reset() noexcept sourcePosition = 0; floatPositionOffset = 0.0f; noteIsOff = false; + filters.clear(); + equalizers.clear(); } float sfz::Voice::getMeanSquaredAverage() const noexcept @@ -544,3 +566,17 @@ uint32_t sfz::Voice::getSourcePosition() const noexcept { return sourcePosition; } + +void sfz::Voice::setMaxFiltersPerVoice(size_t numFilters) +{ + // There are filters in there, this call is unexpected + ASSERT(filters.size() == 0); + filters.reserve(numFilters); +} + +void sfz::Voice::setMaxEQsPerVoice(size_t numFilters) +{ + // There are filters in there, this call is unexpected + ASSERT(filters.size() == 0); + filters.reserve(numFilters); +} diff --git a/src/sfizz/Voice.h b/src/sfizz/Voice.h index 2ac5fe88..caaaee72 100644 --- a/src/sfizz/Voice.h +++ b/src/sfizz/Voice.h @@ -196,6 +196,18 @@ public: * @return */ const Region* getRegion() const noexcept { return region; } + /** + * @brief Set the max number of filters per voice + * + * @param numFilters + */ + void setMaxFiltersPerVoice(size_t numFilters); + /** + * @brief Set the max number of EQs per voice + * + * @param numFilters + */ + void setMaxEQsPerVoice(size_t numEQs); private: /** * @brief Fill a span with data from a file source. This is the first step @@ -283,6 +295,9 @@ private: Resources& resources; + std::vector filters; + std::vector equalizers; + ADSREnvelope egEnvelope; LinearEnvelope amplitudeEnvelope; // linear events LinearEnvelope crossfadeEnvelope;