aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--alc/effects/autowah.cpp45
-rw-r--r--alc/effects/compressor.cpp33
-rw-r--r--alc/effects/equalizer.cpp56
-rw-r--r--alc/effects/modulator.cpp46
-rw-r--r--alc/effects/vmorpher.cpp43
-rw-r--r--core/device.h36
-rw-r--r--core/mixer.h18
7 files changed, 173 insertions, 104 deletions
diff --git a/alc/effects/autowah.cpp b/alc/effects/autowah.cpp
index b5e259f4..8dfee45d 100644
--- a/alc/effects/autowah.cpp
+++ b/alc/effects/autowah.cpp
@@ -65,14 +65,16 @@ struct AutowahState final : public EffectState {
} mEnv[BufferLineSize];
struct {
+ uint mTargetChannel{INVALID_CHANNEL_INDEX};
+
/* Effect filters' history. */
struct {
float z1, z2;
- } Filter;
+ } mFilter;
/* Effect gains for each output channel */
- float CurrentGains[MaxAmbiChannels];
- float TargetGains[MaxAmbiChannels];
+ float mCurrentGain;
+ float mTargetGain;
} mChans[MaxAmbiChannels];
/* Effects buffers */
@@ -108,9 +110,10 @@ void AutowahState::deviceUpdate(const DeviceBase*, const Buffer&)
for(auto &chan : mChans)
{
- std::fill(std::begin(chan.CurrentGains), std::end(chan.CurrentGains), 0.0f);
- chan.Filter.z1 = 0.0f;
- chan.Filter.z2 = 0.0f;
+ chan.mTargetChannel = INVALID_CHANNEL_INDEX;
+ chan.mFilter.z1 = 0.0f;
+ chan.mFilter.z2 = 0.0f;
+ chan.mCurrentGain = 0.0f;
}
}
@@ -131,9 +134,12 @@ void AutowahState::update(const ContextBase *context, const EffectSlot *slot,
mBandwidthNorm = (MaxFreq-MinFreq) / frequency;
mOutTarget = target.Main->Buffer;
- auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
- { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
- SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
+ auto set_channel = [this](size_t idx, uint target, float gain)
+ {
+ mChans[idx].mTargetChannel = target;
+ mChans[idx].mTargetGain = gain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void AutowahState::process(const size_t samplesToDo,
@@ -165,17 +171,24 @@ void AutowahState::process(const size_t samplesToDo,
}
mEnvDelay = env_delay;
- auto chandata = std::addressof(mChans[0]);
+ auto chandata = std::begin(mChans);
for(const auto &insamples : samplesIn)
{
+ const size_t outidx{chandata->mTargetChannel};
+ if(outidx == INVALID_CHANNEL_INDEX)
+ {
+ ++chandata;
+ continue;
+ }
+
/* This effectively inlines BiquadFilter_setParams for a peaking
* filter and BiquadFilter_processC. The alpha and cosine components
* for the filter coefficients were previously calculated with the
* envelope. Because the filter changes for each sample, the
* coefficients are transient and don't need to be held.
*/
- float z1{chandata->Filter.z1};
- float z2{chandata->Filter.z2};
+ float z1{chandata->mFilter.z1};
+ float z2{chandata->mFilter.z2};
for(size_t i{0u};i < samplesToDo;i++)
{
@@ -197,12 +210,12 @@ void AutowahState::process(const size_t samplesToDo,
z2 = input*(b[2]/a[0]) - output*(a[2]/a[0]);
mBufferOut[i] = output;
}
- chandata->Filter.z1 = z1;
- chandata->Filter.z2 = z2;
+ chandata->mFilter.z1 = z1;
+ chandata->mFilter.z2 = z2;
/* Now, mix the processed sound data to the output. */
- MixSamples({mBufferOut, samplesToDo}, samplesOut, chandata->CurrentGains,
- chandata->TargetGains, samplesToDo, 0);
+ MixSamples({mBufferOut, samplesToDo}, {&samplesOut[outidx], 1}, &chandata->mCurrentGain,
+ &chandata->mTargetGain, samplesToDo, 0);
++chandata;
}
}
diff --git a/alc/effects/compressor.cpp b/alc/effects/compressor.cpp
index 6202e58e..a4333539 100644
--- a/alc/effects/compressor.cpp
+++ b/alc/effects/compressor.cpp
@@ -64,7 +64,10 @@ namespace {
struct CompressorState final : public EffectState {
/* Effect gains for each channel */
- float mGain[MaxAmbiChannels][MaxAmbiChannels]{};
+ struct {
+ uint mTarget{INVALID_CHANNEL_INDEX};
+ float mGain{1.0f};
+ } mChans[MaxAmbiChannels];
/* Effect parameters */
bool mEnabled{true};
@@ -103,9 +106,12 @@ void CompressorState::update(const ContextBase*, const EffectSlot *slot,
mEnabled = props->Compressor.OnOff;
mOutTarget = target.Main->Buffer;
- auto set_gains = [slot,target](auto &gains, al::span<const float,MaxAmbiChannels> coeffs)
- { ComputePanGains(target.Main, coeffs.data(), slot->Gain, gains); };
- SetAmbiPanIdentity(std::begin(mGain), slot->Wet.Buffer.size(), set_gains);
+ auto set_channel = [this](size_t idx, uint target, float gain)
+ {
+ mChans[idx].mTarget = target;
+ mChans[idx].mGain = gain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void CompressorState::process(const size_t samplesToDo,
@@ -158,19 +164,22 @@ void CompressorState::process(const size_t samplesToDo,
mEnvFollower = env;
/* Now compress the signal amplitude to output. */
- auto changains = std::addressof(mGain[0]);
+ auto chan = std::cbegin(mChans);
for(const auto &input : samplesIn)
{
- const float *outgains{*(changains++)};
- for(FloatBufferLine &output : samplesOut)
+ const size_t outidx{chan->mTarget};
+ if(outidx != INVALID_CHANNEL_INDEX)
{
- const float gain{*(outgains++)};
+ const float *RESTRICT src{input.data() + base};
+ float *RESTRICT dst{samplesOut[outidx].data() + base};
+ const float gain{chan->mGain};
if(!(std::fabs(gain) > GainSilenceThreshold))
- continue;
-
- for(size_t i{0u};i < td;i++)
- output[base+i] += input[base+i] * gains[i] * gain;
+ {
+ for(size_t i{0u};i < td;i++)
+ dst[i] += src[i] * gains[i] * gain;
+ }
}
+ ++chan;
}
base += td;
diff --git a/alc/effects/equalizer.cpp b/alc/effects/equalizer.cpp
index 4bce34a2..de067911 100644
--- a/alc/effects/equalizer.cpp
+++ b/alc/effects/equalizer.cpp
@@ -87,12 +87,14 @@ namespace {
struct EqualizerState final : public EffectState {
struct {
+ uint mTargetChannel{INVALID_CHANNEL_INDEX};
+
/* Effect parameters */
- BiquadFilter filter[4];
+ BiquadFilter mFilter[4];
/* Effect gains for each channel */
- float CurrentGains[MaxAmbiChannels]{};
- float TargetGains[MaxAmbiChannels]{};
+ float mCurrentGain{};
+ float mTargetGain{};
} mChans[MaxAmbiChannels];
alignas(16) FloatBufferLine mSampleBuffer{};
@@ -111,8 +113,10 @@ void EqualizerState::deviceUpdate(const DeviceBase*, const Buffer&)
{
for(auto &e : mChans)
{
- std::for_each(std::begin(e.filter), std::end(e.filter), std::mem_fn(&BiquadFilter::clear));
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
+ e.mTargetChannel = INVALID_CHANNEL_INDEX;
+ std::for_each(std::begin(e.mFilter), std::end(e.mFilter),
+ std::mem_fn(&BiquadFilter::clear));
+ e.mCurrentGain = 0.0f;
}
}
@@ -131,48 +135,56 @@ void EqualizerState::update(const ContextBase *context, const EffectSlot *slot,
*/
gain = std::sqrt(props->Equalizer.LowGain);
f0norm = props->Equalizer.LowCutoff / frequency;
- mChans[0].filter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f);
+ mChans[0].mFilter[0].setParamsFromSlope(BiquadType::LowShelf, f0norm, gain, 0.75f);
gain = std::sqrt(props->Equalizer.Mid1Gain);
f0norm = props->Equalizer.Mid1Center / frequency;
- mChans[0].filter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
+ mChans[0].mFilter[1].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
props->Equalizer.Mid1Width);
gain = std::sqrt(props->Equalizer.Mid2Gain);
f0norm = props->Equalizer.Mid2Center / frequency;
- mChans[0].filter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
+ mChans[0].mFilter[2].setParamsFromBandwidth(BiquadType::Peaking, f0norm, gain,
props->Equalizer.Mid2Width);
gain = std::sqrt(props->Equalizer.HighGain);
f0norm = props->Equalizer.HighCutoff / frequency;
- mChans[0].filter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f);
+ mChans[0].mFilter[3].setParamsFromSlope(BiquadType::HighShelf, f0norm, gain, 0.75f);
/* Copy the filter coefficients for the other input channels. */
for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
{
- mChans[i].filter[0].copyParamsFrom(mChans[0].filter[0]);
- mChans[i].filter[1].copyParamsFrom(mChans[0].filter[1]);
- mChans[i].filter[2].copyParamsFrom(mChans[0].filter[2]);
- mChans[i].filter[3].copyParamsFrom(mChans[0].filter[3]);
+ mChans[i].mFilter[0].copyParamsFrom(mChans[0].mFilter[0]);
+ mChans[i].mFilter[1].copyParamsFrom(mChans[0].mFilter[1]);
+ mChans[i].mFilter[2].copyParamsFrom(mChans[0].mFilter[2]);
+ mChans[i].mFilter[3].copyParamsFrom(mChans[0].mFilter[3]);
}
mOutTarget = target.Main->Buffer;
- auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
- { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
- SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
+ auto set_channel = [this](size_t idx, uint target, float gain)
+ {
+ mChans[idx].mTargetChannel = target;
+ mChans[idx].mTargetGain = gain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void EqualizerState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
const al::span<float> buffer{mSampleBuffer.data(), samplesToDo};
- auto chan = std::addressof(mChans[0]);
+ auto chan = std::begin(mChans);
for(const auto &input : samplesIn)
{
- const al::span<const float> inbuf{input.data(), samplesToDo};
- DualBiquad{chan->filter[0], chan->filter[1]}.process(inbuf, buffer.begin());
- DualBiquad{chan->filter[2], chan->filter[3]}.process(buffer, buffer.begin());
-
- MixSamples(buffer, samplesOut, chan->CurrentGains, chan->TargetGains, samplesToDo, 0u);
+ const size_t outidx{chan->mTargetChannel};
+ if(outidx != INVALID_CHANNEL_INDEX)
+ {
+ const al::span<const float> inbuf{input.data(), samplesToDo};
+ DualBiquad{chan->mFilter[0], chan->mFilter[1]}.process(inbuf, buffer.begin());
+ DualBiquad{chan->mFilter[2], chan->mFilter[3]}.process(buffer, buffer.begin());
+
+ MixSamples(buffer, {&samplesOut[outidx], 1}, &chan->mCurrentGain, &chan->mTargetGain,
+ samplesToDo, 0u);
+ }
++chan;
}
}
diff --git a/alc/effects/modulator.cpp b/alc/effects/modulator.cpp
index c854f1e9..29009247 100644
--- a/alc/effects/modulator.cpp
+++ b/alc/effects/modulator.cpp
@@ -84,10 +84,12 @@ struct ModulatorState final : public EffectState {
uint mStep{1};
struct {
- BiquadFilter Filter;
+ uint mTargetChannel{INVALID_CHANNEL_INDEX};
- float CurrentGains[MaxAmbiChannels]{};
- float TargetGains[MaxAmbiChannels]{};
+ BiquadFilter mFilter;
+
+ float mCurrentGain{};
+ float mTargetGain{};
} mChans[MaxAmbiChannels];
@@ -104,8 +106,9 @@ void ModulatorState::deviceUpdate(const DeviceBase*, const Buffer&)
{
for(auto &e : mChans)
{
- e.Filter.clear();
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
+ e.mTargetChannel = INVALID_CHANNEL_INDEX;
+ e.mFilter.clear();
+ e.mCurrentGain = 0.0f;
}
}
@@ -129,14 +132,17 @@ void ModulatorState::update(const ContextBase *context, const EffectSlot *slot,
float f0norm{props->Modulator.HighPassCutoff / static_cast<float>(device->Frequency)};
f0norm = clampf(f0norm, 1.0f/512.0f, 0.49f);
/* Bandwidth value is constant in octaves. */
- mChans[0].Filter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f);
+ mChans[0].mFilter.setParamsFromBandwidth(BiquadType::HighPass, f0norm, 1.0f, 0.75f);
for(size_t i{1u};i < slot->Wet.Buffer.size();++i)
- mChans[i].Filter.copyParamsFrom(mChans[0].Filter);
+ mChans[i].mFilter.copyParamsFrom(mChans[0].mFilter);
mOutTarget = target.Main->Buffer;
- auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
- { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
- SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
+ auto set_channel = [this](size_t idx, uint target, float gain)
+ {
+ mChans[idx].mTargetChannel = target;
+ mChans[idx].mTargetGain = gain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void ModulatorState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
@@ -153,14 +159,18 @@ void ModulatorState::process(const size_t samplesToDo, const al::span<const Floa
auto chandata = std::begin(mChans);
for(const auto &input : samplesIn)
{
- alignas(16) float temps[MAX_UPDATE_SAMPLES];
-
- chandata->Filter.process({&input[base], td}, temps);
- for(size_t i{0u};i < td;i++)
- temps[i] *= modsamples[i];
-
- MixSamples({temps, td}, samplesOut, chandata->CurrentGains, chandata->TargetGains,
- samplesToDo-base, base);
+ const size_t outidx{chandata->mTargetChannel};
+ if(outidx != INVALID_CHANNEL_INDEX)
+ {
+ alignas(16) float temps[MAX_UPDATE_SAMPLES];
+
+ chandata->mFilter.process({&input[base], td}, temps);
+ for(size_t i{0u};i < td;i++)
+ temps[i] *= modsamples[i];
+
+ MixSamples({temps, td}, {&samplesOut[outidx], 1}, &chandata->mCurrentGain,
+ &chandata->mTargetGain, samplesToDo-base, base);
+ }
++chandata;
}
diff --git a/alc/effects/vmorpher.cpp b/alc/effects/vmorpher.cpp
index 9834c335..869c004e 100644
--- a/alc/effects/vmorpher.cpp
+++ b/alc/effects/vmorpher.cpp
@@ -143,12 +143,14 @@ struct FormantFilter
struct VmorpherState final : public EffectState {
struct {
+ uint mTargetChannel{INVALID_CHANNEL_INDEX};
+
/* Effect parameters */
- FormantFilter Formants[NUM_FILTERS][NUM_FORMANTS];
+ FormantFilter mFormants[NUM_FILTERS][NUM_FORMANTS];
/* Effect gains for each channel */
- float CurrentGains[MaxAmbiChannels]{};
- float TargetGains[MaxAmbiChannels]{};
+ float mCurrentGain{};
+ float mTargetGain{};
} mChans[MaxAmbiChannels];
void (*mGetSamples)(float*RESTRICT, uint, const uint, size_t){};
@@ -229,11 +231,12 @@ void VmorpherState::deviceUpdate(const DeviceBase*, const Buffer&)
{
for(auto &e : mChans)
{
- std::for_each(std::begin(e.Formants[VOWEL_A_INDEX]), std::end(e.Formants[VOWEL_A_INDEX]),
+ e.mTargetChannel = INVALID_CHANNEL_INDEX;
+ std::for_each(std::begin(e.mFormants[VOWEL_A_INDEX]), std::end(e.mFormants[VOWEL_A_INDEX]),
std::mem_fn(&FormantFilter::clear));
- std::for_each(std::begin(e.Formants[VOWEL_B_INDEX]), std::end(e.Formants[VOWEL_B_INDEX]),
+ std::for_each(std::begin(e.mFormants[VOWEL_B_INDEX]), std::end(e.mFormants[VOWEL_B_INDEX]),
std::mem_fn(&FormantFilter::clear));
- std::fill(std::begin(e.CurrentGains), std::end(e.CurrentGains), 0.0f);
+ e.mCurrentGain = 0.0f;
}
}
@@ -265,14 +268,17 @@ void VmorpherState::update(const ContextBase *context, const EffectSlot *slot,
/* Copy the filter coefficients to the input channels. */
for(size_t i{0u};i < slot->Wet.Buffer.size();++i)
{
- std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].Formants[VOWEL_A_INDEX]));
- std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].Formants[VOWEL_B_INDEX]));
+ std::copy(vowelA.begin(), vowelA.end(), std::begin(mChans[i].mFormants[VOWEL_A_INDEX]));
+ std::copy(vowelB.begin(), vowelB.end(), std::begin(mChans[i].mFormants[VOWEL_B_INDEX]));
}
mOutTarget = target.Main->Buffer;
- auto set_gains = [slot,target](auto &chan, al::span<const float,MaxAmbiChannels> coeffs)
- { ComputePanGains(target.Main, coeffs.data(), slot->Gain, chan.TargetGains); };
- SetAmbiPanIdentity(std::begin(mChans), slot->Wet.Buffer.size(), set_gains);
+ auto set_channel = [this](size_t idx, uint target, float gain)
+ {
+ mChans[idx].mTargetChannel = target;
+ mChans[idx].mTargetGain = gain;
+ };
+ target.Main->setAmbiMixParams(slot->Wet, slot->Gain, set_channel);
}
void VmorpherState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
@@ -291,8 +297,15 @@ void VmorpherState::process(const size_t samplesToDo, const al::span<const Float
auto chandata = std::begin(mChans);
for(const auto &input : samplesIn)
{
- auto& vowelA = chandata->Formants[VOWEL_A_INDEX];
- auto& vowelB = chandata->Formants[VOWEL_B_INDEX];
+ const size_t outidx{chandata->mTargetChannel};
+ if(outidx == INVALID_CHANNEL_INDEX)
+ {
+ ++chandata;
+ continue;
+ }
+
+ auto& vowelA = chandata->mFormants[VOWEL_A_INDEX];
+ auto& vowelB = chandata->mFormants[VOWEL_B_INDEX];
/* Process first vowel. */
std::fill_n(std::begin(mSampleBufferA), td, 0.0f);
@@ -313,8 +326,8 @@ void VmorpherState::process(const size_t samplesToDo, const al::span<const Float
blended[i] = lerpf(mSampleBufferA[i], mSampleBufferB[i], mLfo[i]);
/* Now, mix the processed sound data to the output. */
- MixSamples({blended, td}, samplesOut, chandata->CurrentGains, chandata->TargetGains,
- samplesToDo-base, base);
+ MixSamples({blended, td}, {&samplesOut[outidx], 1}, &chandata->mCurrentGain,
+ &chandata->mTargetGain, samplesToDo-base, base);
++chandata;
}
diff --git a/core/device.h b/core/device.h
index 6317067e..7d50c54d 100644
--- a/core/device.h
+++ b/core/device.h
@@ -95,6 +95,8 @@ struct DistanceComp {
};
+#define INVALID_CHANNEL_INDEX ~0u
+
struct BFChannelConfig {
float Scale;
uint Index;
@@ -105,6 +107,37 @@ struct MixParams {
std::array<BFChannelConfig,MaxAmbiChannels> AmbiMap{};
al::span<FloatBufferLine> Buffer;
+
+ /**
+ * Helper to set an identity/pass-through panning for ambisonic mixing. The
+ * source is expected to be a 3D ACN/N3D ambisonic buffer, and for each
+ * channel [0...count), the given functor is called with the source channel
+ * index, destination channel index, and the gain for that channel. If the
+ * destination channel is INVALID_CHANNEL_INDEX, the given source channel
+ * is not used for output.
+ */
+ template<typename F>
+ void setAmbiMixParams(const MixParams &inmix, const float gainbase, F func) const
+ {
+ const size_t numIn{inmix.Buffer.size()};
+ const size_t numOut{Buffer.size()};
+ for(size_t i{0};i < numIn;++i)
+ {
+ auto idx = INVALID_CHANNEL_INDEX;
+ auto gain = 0.0f;
+
+ for(size_t j{0};j < numOut;++j)
+ {
+ if(AmbiMap[j].Index == inmix.AmbiMap[i].Index)
+ {
+ idx = static_cast<uint>(j);
+ gain = AmbiMap[j].Scale * gainbase;
+ break;
+ }
+ }
+ func(i, idx, gain);
+ }
+ }
};
struct RealMixParams {
@@ -303,13 +336,10 @@ private:
uint renderSamples(const uint numSamples);
};
-
/* Must be less than 15 characters (16 including terminating null) for
* compatibility with pthread_setname_np limitations. */
#define MIXER_THREAD_NAME "alsoft-mixer"
#define RECORD_THREAD_NAME "alsoft-record"
-#define INVALID_CHANNEL_INDEX ~0u
-
#endif /* CORE_DEVICE_H */
diff --git a/core/mixer.h b/core/mixer.h
index 8624996f..66bcb170 100644
--- a/core/mixer.h
+++ b/core/mixer.h
@@ -92,22 +92,4 @@ inline std::array<float,MaxAmbiChannels> CalcAngleCoeffs(const float azimuth,
void ComputePanGains(const MixParams *mix, const float*RESTRICT coeffs, const float ingain,
const al::span<float,MaxAmbiChannels> gains);
-
-/** Helper to set an identity/pass-through panning for ambisonic mixing (3D input). */
-template<typename T, typename I, typename F>
-auto SetAmbiPanIdentity(T iter, I count, F func) -> std::enable_if_t<std::is_integral<I>::value>
-{
- if(count < 1) return;
-
- std::array<float,MaxAmbiChannels> coeffs{{1.0f}};
- func(*iter, coeffs);
- ++iter;
- for(I i{1};i < count;++i,++iter)
- {
- coeffs[i-1] = 0.0f;
- coeffs[i ] = 1.0f;
- func(*iter, coeffs);
- }
-}
-
#endif /* CORE_MIXER_H */