diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 94d9eff8..b456bf15 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,6 +3,7 @@ include(CheckLibraryExists) set (SFIZZ_SOURCES sfizz/Synth.cpp sfizz/FilePool.cpp + sfizz/FilterPool.cpp sfizz/Region.cpp sfizz/Voice.cpp sfizz/ScopedFTZ.cpp diff --git a/src/sfizz/CCMap.h b/src/sfizz/CCMap.h index c89df41a..34bf5b7c 100644 --- a/src/sfizz/CCMap.h +++ b/src/sfizz/CCMap.h @@ -86,7 +86,9 @@ public: */ bool contains(int index) const noexcept { return container.find(index) != container.end(); } typename std::map::iterator begin() { return container.begin(); } + typename std::map::const_iterator begin() const { return container.cbegin(); } typename std::map::iterator end() { return container.end(); } + typename std::map::const_iterator end() const { return container.cend(); } private: const ValueType defaultValue; std::map container; diff --git a/src/sfizz/Config.h b/src/sfizz/Config.h index 92765c35..e9818e39 100644 --- a/src/sfizz/Config.h +++ b/src/sfizz/Config.h @@ -53,6 +53,7 @@ namespace config { constexpr uint8_t numCCs { 143 }; constexpr int chunkSize { 1024 }; constexpr float defaultAmpEGRelease { 0.02f }; + constexpr int defaultNumFilters { maxVoices * 2 }; /** 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/Defaults.h b/src/sfizz/Defaults.h index 9ed5cf72..2b734d91 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -131,7 +131,7 @@ namespace Default constexpr float filterGain { 0 }; constexpr int filterKeytrack { 0 }; constexpr uint8_t filterKeycenter { 60 }; - constexpr int filterRandom { 60 }; + constexpr int filterRandom { 0 }; constexpr int filterVeltrack { 0 }; constexpr int filterCutoffCC { 0 }; constexpr float filterResonanceCC { 0 }; diff --git a/src/sfizz/FilterPool.cpp b/src/sfizz/FilterPool.cpp new file mode 100644 index 00000000..d43ec3e0 --- /dev/null +++ b/src/sfizz/FilterPool.cpp @@ -0,0 +1,97 @@ +#include "FilterPool.h" +#include "SIMDHelpers.h" +#include "absl/algorithm/container.h" + +sfz::FilterHolder::FilterHolder(const MidiState& midiState) +: midiState(midiState) +{ + filter.setChannels(2); +} + +void sfz::FilterHolder::reset() +{ + filter.clear(); +} + +void sfz::FilterHolder::setup(const FilterDescription& description, int noteNumber, uint8_t velocity) +{ + reset(); + this->description = &description; + filter.setType(description.type); + + baseCutoff = description.cutoff; + if (description.random != 0) { + dist.param(filterRandomDist::param_type(0, description.random)); + baseCutoff *= centsFactor(dist(Random::randomGenerator)); + } + const auto keytrack = description.keytrack * (noteNumber - description.keycenter); + baseCutoff *= centsFactor(keytrack); + const auto veltrack = description.veltrack * velocity; + baseCutoff *= centsFactor(veltrack); + + baseGain = description.gain; + baseResonance = description.resonance; +} + +void sfz::FilterHolder::process(const float** inputs, float** outputs, unsigned numFrames) +{ + if (description == nullptr) { + for (unsigned channelIdx = 0; channelIdx < filter.channels(); channelIdx++) + copy({ inputs[channelIdx], numFrames }, { outputs[channelIdx], numFrames }); + return; + } + + // TODO: Once the midistate envelopes are done, add modulation in there! + // For now we take the last value + auto cutoff = baseCutoff; + for (auto& mod: description->cutoffCC) { + cutoff *= centsFactor(midiState.getCCValue(mod.first) * mod.second); + } + + auto resonance = baseResonance; + for (auto& mod: description->resonanceCC) { + resonance += midiState.getCCValue(mod.first) * mod.second; + } + + auto gain = baseGain; + for (auto& mod: description->gainCC) { + gain += midiState.getCCValue(mod.first) * mod.second; + } + + filter.process(inputs, outputs, cutoff, resonance, gain,numFrames); +} + + +sfz::FilterPool::FilterPool(const MidiState& state, int numFilters) +: midiState(state) +{ + setNumFilters(numFilters); +} + +sfz::FilterHolderPtr sfz::FilterPool::getFilter(const FilterDescription& description, int noteNumber, uint8_t velocity) +{ + auto filter = absl::c_find_if(filters, [](const FilterHolderPtr& holder) { + return holder.use_count() == 1; + }); + + if (filter == filters.end()) + return {}; + + (**filter).setup(description, noteNumber, velocity); + return *filter; +} + +int sfz::FilterPool::getActiveFilters() const +{ + return absl::c_count_if(filters, [](const FilterHolderPtr& holder) { + return holder.use_count() > 1; + }); +} + +void sfz::FilterPool::setNumFilters(int numFilters) +{ + filters.clear(); + filters.reserve(numFilters); + for (int i = 0; i < numFilters; ++i) + filters.emplace_back(std::make_shared(midiState)); +} diff --git a/src/sfizz/FilterPool.h b/src/sfizz/FilterPool.h new file mode 100644 index 00000000..8bd3f70e --- /dev/null +++ b/src/sfizz/FilterPool.h @@ -0,0 +1,44 @@ +#pragma once +#include "SfzFilter.h" +#include "FilterDescription.h" +#include "MidiState.h" +#include +#include + +namespace sfz +{ + +class FilterHolder +{ +public: + FilterHolder() = delete; + FilterHolder(const MidiState& state); + void reset(); + void setup(const FilterDescription& description, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + void process(const float** inputs, float** outputs, unsigned numFrames); +private: + const MidiState& midiState; + const FilterDescription* description; + Filter filter; + float baseCutoff { Default::filterCutoff }; + float baseResonance { Default::filterResonance }; + float baseGain { Default::filterGain }; + using filterRandomDist = std::uniform_int_distribution; + filterRandomDist dist { 0, sfz::Default::filterRandom }; +}; + +using FilterHolderPtr = std::shared_ptr; + +class FilterPool +{ +public: + FilterPool() = delete; + FilterPool(const MidiState& state, int numFilters = config::defaultNumFilters); + FilterHolderPtr getFilter(const FilterDescription& description, int noteNumber = static_cast(Default::filterKeycenter), uint8_t velocity = 0); + int getActiveFilters() const; + void setNumFilters(int numFilters); +private: + const MidiState& midiState; + std::vector filters; +}; +}