Added EQPool
This commit is contained in:
parent
3d60de459f
commit
ad798c38df
3 changed files with 259 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ set (SFIZZ_SOURCES
|
|||
sfizz/Synth.cpp
|
||||
sfizz/FilePool.cpp
|
||||
sfizz/FilterPool.cpp
|
||||
sfizz/EQPool.cpp
|
||||
sfizz/Region.cpp
|
||||
sfizz/Voice.cpp
|
||||
sfizz/ScopedFTZ.cpp
|
||||
|
|
|
|||
137
src/sfizz/EQPool.cpp
Normal file
137
src/sfizz/EQPool.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#include "EQPool.h"
|
||||
#include "AtomicGuard.h"
|
||||
#include <thread>
|
||||
#include "absl/algorithm/container.h"
|
||||
#include "SIMDHelpers.h"
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
sfz::EQHolder::EQHolder(const MidiState& state)
|
||||
:midiState(state)
|
||||
{
|
||||
eq.setChannels(2);
|
||||
}
|
||||
|
||||
void sfz::EQHolder::reset()
|
||||
{
|
||||
eq.clear();
|
||||
}
|
||||
|
||||
void sfz::EQHolder::setup(const EQDescription& description, uint8_t velocity)
|
||||
{
|
||||
reset();
|
||||
this->description = &description;
|
||||
const auto normalizedVelocity = normalizeVelocity(velocity);
|
||||
|
||||
baseBandwidth = description.bandwidth;
|
||||
baseGain = Default::eqGainRange.clamp(description.gain + normalizedVelocity * description.vel2gain);
|
||||
baseFrequency = Default::eqFrequencyRange.clamp(description.frequency + normalizedVelocity * description.vel2frequency);
|
||||
}
|
||||
|
||||
void sfz::EQHolder::process(const float** inputs, float** outputs, unsigned numFrames)
|
||||
{
|
||||
if (description == nullptr) {
|
||||
for (unsigned channelIdx = 0; channelIdx < eq.channels(); channelIdx++)
|
||||
copy<float>({ 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
|
||||
lastFrequency = baseFrequency;
|
||||
for (auto& mod: description->frequencyCC) {
|
||||
lastFrequency += midiState.getCCValue(mod.first) * mod.second;
|
||||
}
|
||||
lastFrequency = Default::eqFrequencyRange.clamp(lastFrequency);
|
||||
|
||||
lastBandwidth = baseBandwidth;
|
||||
for (auto& mod: description->bandwidthCC) {
|
||||
lastBandwidth += midiState.getCCValue(mod.first) * mod.second;
|
||||
}
|
||||
lastBandwidth = Default::eqBandwidthRange.clamp(lastBandwidth);
|
||||
|
||||
lastGain = baseGain;
|
||||
for (auto& mod: description->gainCC) {
|
||||
lastGain += midiState.getCCValue(mod.first) * mod.second;
|
||||
}
|
||||
lastGain = Default::eqGainRange.clamp(lastGain);
|
||||
|
||||
eq.process(inputs, outputs, lastFrequency, lastBandwidth, lastGain, numFrames);
|
||||
}
|
||||
float sfz::EQHolder::getLastFrequency() const
|
||||
{
|
||||
return lastFrequency;
|
||||
}
|
||||
float sfz::EQHolder::getLastBandwidth() const
|
||||
{
|
||||
return lastBandwidth;
|
||||
}
|
||||
float sfz::EQHolder::getLastGain() const
|
||||
{
|
||||
return lastGain;
|
||||
}
|
||||
void sfz::EQHolder::setSampleRate(float sampleRate)
|
||||
{
|
||||
eq.init(static_cast<double>(sampleRate));
|
||||
}
|
||||
|
||||
sfz::EQPool::EQPool(const MidiState& state, int numEQs)
|
||||
: midiState(state)
|
||||
{
|
||||
setnumEQs(numEQs);
|
||||
}
|
||||
|
||||
sfz::EQHolderPtr sfz::EQPool::getEQ(const EQDescription& description, uint8_t velocity)
|
||||
{
|
||||
AtomicGuard guard { givingOutEQs };
|
||||
if (!canGiveOutEQs)
|
||||
return {};
|
||||
|
||||
auto eq = absl::c_find_if(eqs, [](const EQHolderPtr& holder) {
|
||||
return holder.use_count() == 1;
|
||||
});
|
||||
|
||||
if (eq == eqs.end())
|
||||
return {};
|
||||
|
||||
(**eq).setup(description, velocity);
|
||||
return *eq;
|
||||
}
|
||||
|
||||
size_t sfz::EQPool::getActiveEQs() const
|
||||
{
|
||||
return absl::c_count_if(eqs, [](const EQHolderPtr& holder) {
|
||||
return holder.use_count() > 1;
|
||||
});
|
||||
}
|
||||
|
||||
size_t sfz::EQPool::setnumEQs(size_t numEQs)
|
||||
{
|
||||
AtomicDisabler disabler { canGiveOutEQs };
|
||||
|
||||
while(givingOutEQs)
|
||||
std::this_thread::sleep_for(1ms);
|
||||
|
||||
auto eqIterator = eqs.begin();
|
||||
auto eqSentinel = eqs.rbegin();
|
||||
while (eqIterator < eqSentinel.base()) {
|
||||
if (eqIterator->use_count() == 1) {
|
||||
std::iter_swap(eqIterator, eqSentinel);
|
||||
++eqSentinel;
|
||||
} else {
|
||||
++eqIterator;
|
||||
}
|
||||
}
|
||||
|
||||
eqs.resize(std::distance(eqs.begin(), eqSentinel.base()));
|
||||
for (size_t i = eqs.size(); i < numEQs; ++i) {
|
||||
eqs.emplace_back(std::make_shared<EQHolder>(midiState));
|
||||
eqs.back()->setSampleRate(sampleRate);
|
||||
}
|
||||
|
||||
return eqs.size();
|
||||
}
|
||||
void sfz::EQPool::setSampleRate(float sampleRate)
|
||||
{
|
||||
for (auto& eq: eqs)
|
||||
eq->setSampleRate(sampleRate);
|
||||
}
|
||||
121
src/sfizz/EQPool.h
Normal file
121
src/sfizz/EQPool.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
#include "SfzFilter.h"
|
||||
#include "EQDescription.h"
|
||||
#include "MidiState.h"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace sfz
|
||||
{
|
||||
|
||||
class EQHolder
|
||||
{
|
||||
public:
|
||||
EQHolder() = delete;
|
||||
EQHolder(const MidiState& state);
|
||||
/**
|
||||
* @brief Setup a new EQ based on an EQ description.
|
||||
*
|
||||
* @param description the EQ description
|
||||
*/
|
||||
void setup(const EQDescription& description, uint8_t velocity);
|
||||
/**
|
||||
* @brief Process a block of stereo inputs
|
||||
*
|
||||
* @param inputs
|
||||
* @param outputs
|
||||
* @param numFrames
|
||||
*/
|
||||
void process(const float** inputs, float** outputs, unsigned numFrames);
|
||||
/**
|
||||
* @brief Returns the last value of the frequency for the EQ
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
float getLastFrequency() const;
|
||||
/**
|
||||
* @brief Returns the last value of the bandwitdh for the EQ
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
float getLastBandwidth() const;
|
||||
/**
|
||||
* @brief Returns the last value of the gain for the EQ
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
float getLastGain() const;
|
||||
/**
|
||||
* @brief Set the sample rate for the EQ
|
||||
*
|
||||
* @param sampleRate
|
||||
*/
|
||||
void setSampleRate(float sampleRate);
|
||||
private:
|
||||
/**
|
||||
* Reset the filter. Is called internally when using setup().
|
||||
*/
|
||||
void reset();
|
||||
const MidiState& midiState;
|
||||
const EQDescription* description;
|
||||
FilterEq eq;
|
||||
float baseBandwidth { Default::eqBandwidth };
|
||||
float baseFrequency { Default::eqFrequency1 };
|
||||
float baseGain { Default::eqGain };
|
||||
float lastBandwidth { Default::eqBandwidth };
|
||||
float lastFrequency { Default::eqFrequency1 };
|
||||
float lastGain { Default::eqGain };
|
||||
};
|
||||
|
||||
using EQHolderPtr = std::shared_ptr<EQHolder>;
|
||||
|
||||
class EQPool
|
||||
{
|
||||
public:
|
||||
EQPool() = delete;
|
||||
/**
|
||||
* @brief Construct a new EQPool object
|
||||
*
|
||||
* @param state the associated midi state
|
||||
* @param numEQs the number of inactive EQs to hold in the pool
|
||||
*/
|
||||
EQPool(const MidiState& state, int numEQs = config::filtersInPool);
|
||||
/**
|
||||
* @brief Get an EQ object to use in Voices
|
||||
*
|
||||
* @param description the filter description to bind to the EQ
|
||||
* @param noteNumber the triggering note number
|
||||
* @param velocity the triggering note velocity
|
||||
* @return EQHolderPtr release this when done with the filter; no deallocation will be done
|
||||
*/
|
||||
EQHolderPtr getEQ(const EQDescription& description, uint8_t velocity);
|
||||
/**
|
||||
* @brief Get the number of active EQs
|
||||
*
|
||||
* @return size_t
|
||||
*/
|
||||
size_t getActiveEQs() const;
|
||||
/**
|
||||
* @brief Set the number of EQs in the pool. This function may sleep and should be called from a background thread.
|
||||
* No EQs will be distributed during the reallocation of EQs. Existing running EQs are kept. If the target
|
||||
* number of EQs is less that the number of active EQs, the function will not remove them and you may need
|
||||
* to call it again after existing EQs have run out.
|
||||
*
|
||||
* @param numEQs
|
||||
* @return size_t the actual number of EQs in the pool
|
||||
*/
|
||||
size_t setnumEQs(size_t numEQs);
|
||||
/**
|
||||
* @brief Set the sample rate for all EQs
|
||||
*
|
||||
* @param sampleRate
|
||||
*/
|
||||
void setSampleRate(float sampleRate);
|
||||
private:
|
||||
std::atomic<bool> givingOutEQs { false };
|
||||
std::atomic<bool> canGiveOutEQs { true };
|
||||
float sampleRate { config::defaultSampleRate };
|
||||
const MidiState& midiState;
|
||||
std::vector<EQHolderPtr> eqs;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue