Add and use mixers that process one input and output channel
This commit is contained in:
parent
58a18ab3c0
commit
23c8a35505
@ -214,8 +214,8 @@ void AutowahState::process(const size_t samplesToDo,
|
||||
chandata->mFilter.z2 = z2;
|
||||
|
||||
/* Now, mix the processed sound data to the output. */
|
||||
MixSamples({mBufferOut, samplesToDo}, {&samplesOut[outidx], 1}, &chandata->mCurrentGain,
|
||||
&chandata->mTargetGain, samplesToDo, 0);
|
||||
MixSamples({mBufferOut, samplesToDo}, samplesOut[outidx].data(), chandata->mCurrentGain,
|
||||
chandata->mTargetGain, samplesToDo);
|
||||
++chandata;
|
||||
}
|
||||
}
|
||||
|
@ -182,8 +182,8 @@ void EqualizerState::process(const size_t samplesToDo, const al::span<const Floa
|
||||
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);
|
||||
MixSamples(buffer, samplesOut[outidx].data(), chan->mCurrentGain, chan->mTargetGain,
|
||||
samplesToDo);
|
||||
}
|
||||
++chan;
|
||||
}
|
||||
|
@ -168,8 +168,8 @@ void ModulatorState::process(const size_t samplesToDo, const al::span<const Floa
|
||||
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);
|
||||
MixSamples({temps, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain,
|
||||
chandata->mTargetGain, samplesToDo-base);
|
||||
}
|
||||
++chandata;
|
||||
}
|
||||
|
@ -326,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[outidx], 1}, &chandata->mCurrentGain,
|
||||
&chandata->mTargetGain, samplesToDo-base, base);
|
||||
MixSamples({blended, td}, samplesOut[outidx].data()+base, chandata->mCurrentGain,
|
||||
chandata->mTargetGain, samplesToDo-base);
|
||||
++chandata;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ struct CTag;
|
||||
|
||||
|
||||
MixerOutFunc MixSamplesOut{Mix_<CTag>};
|
||||
MixerOneFunc MixSamplesOne{Mix_<CTag>};
|
||||
|
||||
|
||||
std::array<float,MaxAmbiChannels> CalcAmbiCoeffs(const float y, const float z, const float x,
|
||||
|
@ -24,6 +24,14 @@ inline void MixSamples(const al::span<const float> InSamples,
|
||||
const size_t Counter, const size_t OutPos)
|
||||
{ MixSamplesOut(InSamples, OutBuffer, CurrentGains, TargetGains, Counter, OutPos); }
|
||||
|
||||
/* Mixer functions that handle one input and one output channel. */
|
||||
using MixerOneFunc = void(*)(const al::span<const float> InSamples, float *OutBuffer,
|
||||
float &CurrentGain, const float TargetGain, const size_t Counter);
|
||||
|
||||
extern MixerOneFunc MixSamplesOne;
|
||||
inline void MixSamples(const al::span<const float> InSamples, float *OutBuffer, float &CurrentGain,
|
||||
const float TargetGain, const size_t Counter)
|
||||
{ MixSamplesOne(InSamples, OutBuffer, CurrentGain, TargetGain, Counter); }
|
||||
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,9 @@ float *Resample_(const InterpState *state, float *RESTRICT src, uint frac, uint
|
||||
template<typename InstTag>
|
||||
void Mix_(const al::span<const float> InSamples, const al::span<FloatBufferLine> OutBuffer,
|
||||
float *CurrentGains, const float *TargetGains, const size_t Counter, const size_t OutPos);
|
||||
template<typename InstTag>
|
||||
void Mix_(const al::span<const float> InSamples, float *OutBuffer, float &CurrentGain,
|
||||
const float TargetGain, const size_t Counter);
|
||||
|
||||
template<typename InstTag>
|
||||
void MixHrtf_(const float *InSamples, float2 *AccumSamples, const uint IrSize,
|
||||
|
@ -198,3 +198,38 @@ void Mix_<CTag>(const al::span<const float> InSamples, const al::span<FloatBuffe
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void Mix_<CTag>(const al::span<const float> InSamples, float *OutBuffer, float &CurrentGain,
|
||||
const float TargetGain, const size_t Counter)
|
||||
{
|
||||
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
|
||||
const auto min_len = minz(Counter, InSamples.size());
|
||||
|
||||
float *RESTRICT dst{al::assume_aligned<16>(OutBuffer)};
|
||||
float gain{CurrentGain};
|
||||
const float step{(TargetGain-gain) * delta};
|
||||
|
||||
size_t pos{0};
|
||||
if(!(std::abs(step) > std::numeric_limits<float>::epsilon()))
|
||||
gain = TargetGain;
|
||||
else
|
||||
{
|
||||
float step_count{0.0f};
|
||||
for(;pos != min_len;++pos)
|
||||
{
|
||||
dst[pos] += InSamples[pos] * (gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGain;
|
||||
else
|
||||
gain += step*step_count;
|
||||
}
|
||||
CurrentGain = gain;
|
||||
|
||||
if(!(std::abs(gain) > GainSilenceThreshold))
|
||||
return;
|
||||
for(;pos != InSamples.size();++pos)
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
|
@ -305,3 +305,80 @@ void Mix_<NEONTag>(const al::span<const float> InSamples, const al::span<FloatBu
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void Mix_<NEONTag>(const al::span<const float> InSamples, float *OutBuffer, float &CurrentGain,
|
||||
const float TargetGain, const size_t Counter)
|
||||
{
|
||||
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
|
||||
const auto min_len = minz(Counter, InSamples.size());
|
||||
const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
|
||||
|
||||
float *RESTRICT dst{al::assume_aligned<16>(OutBuffer)};
|
||||
float gain{CurrentGain};
|
||||
const float step{(TargetGain-gain) * delta};
|
||||
|
||||
size_t pos{0};
|
||||
if(!(std::abs(step) > std::numeric_limits<float>::epsilon()))
|
||||
gain = TargetGain;
|
||||
else
|
||||
{
|
||||
float step_count{0.0f};
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(size_t todo{min_len >> 2})
|
||||
{
|
||||
const float32x4_t four4{vdupq_n_f32(4.0f)};
|
||||
const float32x4_t step4{vdupq_n_f32(step)};
|
||||
const float32x4_t gain4{vdupq_n_f32(gain)};
|
||||
float32x4_t step_count4{vdupq_n_f32(0.0f)};
|
||||
step_count4 = vsetq_lane_f32(1.0f, step_count4, 1);
|
||||
step_count4 = vsetq_lane_f32(2.0f, step_count4, 2);
|
||||
step_count4 = vsetq_lane_f32(3.0f, step_count4, 3);
|
||||
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&InSamples[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&dst[pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, vmlaq_f32(gain4, step4, step_count4));
|
||||
step_count4 = vaddq_f32(step_count4, four4);
|
||||
vst1q_f32(&dst[pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after the
|
||||
* last four mixed samples, so the lowest element represents the
|
||||
* next step count to apply.
|
||||
*/
|
||||
step_count = vgetq_lane_f32(step_count4, 0);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(size_t leftover{min_len&3};leftover;++pos,--leftover)
|
||||
{
|
||||
dst[pos] += InSamples[pos] * (gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGain;
|
||||
else
|
||||
gain += step*step_count;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(size_t leftover{aligned_len&3};leftover;++pos,--leftover)
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
CurrentGain = gain;
|
||||
|
||||
if(!(std::abs(gain) > GainSilenceThreshold))
|
||||
return;
|
||||
if(size_t todo{(InSamples.size()-pos) >> 2})
|
||||
{
|
||||
const float32x4_t gain4 = vdupq_n_f32(gain);
|
||||
do {
|
||||
const float32x4_t val4 = vld1q_f32(&InSamples[pos]);
|
||||
float32x4_t dry4 = vld1q_f32(&dst[pos]);
|
||||
dry4 = vmlaq_f32(dry4, val4, gain4);
|
||||
vst1q_f32(&dst[pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover)
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
|
@ -270,3 +270,79 @@ void Mix_<SSETag>(const al::span<const float> InSamples, const al::span<FloatBuf
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void Mix_<SSETag>(const al::span<const float> InSamples, float *OutBuffer, float &CurrentGain,
|
||||
const float TargetGain, const size_t Counter)
|
||||
{
|
||||
const float delta{(Counter > 0) ? 1.0f / static_cast<float>(Counter) : 0.0f};
|
||||
const auto min_len = minz(Counter, InSamples.size());
|
||||
const auto aligned_len = minz((min_len+3) & ~size_t{3}, InSamples.size()) - min_len;
|
||||
|
||||
float *RESTRICT dst{al::assume_aligned<16>(OutBuffer)};
|
||||
float gain{CurrentGain};
|
||||
const float step{(TargetGain-gain) * delta};
|
||||
|
||||
size_t pos{0};
|
||||
if(!(std::abs(step) > std::numeric_limits<float>::epsilon()))
|
||||
gain = TargetGain;
|
||||
else
|
||||
{
|
||||
float step_count{0.0f};
|
||||
/* Mix with applying gain steps in aligned multiples of 4. */
|
||||
if(size_t todo{min_len >> 2})
|
||||
{
|
||||
const __m128 four4{_mm_set1_ps(4.0f)};
|
||||
const __m128 step4{_mm_set1_ps(step)};
|
||||
const __m128 gain4{_mm_set1_ps(gain)};
|
||||
__m128 step_count4{_mm_setr_ps(0.0f, 1.0f, 2.0f, 3.0f)};
|
||||
do {
|
||||
const __m128 val4{_mm_load_ps(&InSamples[pos])};
|
||||
__m128 dry4{_mm_load_ps(&dst[pos])};
|
||||
|
||||
/* dry += val * (gain + step*step_count) */
|
||||
dry4 = MLA4(dry4, val4, MLA4(gain4, step4, step_count4));
|
||||
|
||||
_mm_store_ps(&dst[pos], dry4);
|
||||
step_count4 = _mm_add_ps(step_count4, four4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
/* NOTE: step_count4 now represents the next four counts after the
|
||||
* last four mixed samples, so the lowest element represents the
|
||||
* next step count to apply.
|
||||
*/
|
||||
step_count = _mm_cvtss_f32(step_count4);
|
||||
}
|
||||
/* Mix with applying left over gain steps that aren't aligned multiples of 4. */
|
||||
for(size_t leftover{min_len&3};leftover;++pos,--leftover)
|
||||
{
|
||||
dst[pos] += InSamples[pos] * (gain + step*step_count);
|
||||
step_count += 1.0f;
|
||||
}
|
||||
if(pos == Counter)
|
||||
gain = TargetGain;
|
||||
else
|
||||
gain += step*step_count;
|
||||
|
||||
/* Mix until pos is aligned with 4 or the mix is done. */
|
||||
for(size_t leftover{aligned_len&3};leftover;++pos,--leftover)
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
CurrentGain = gain;
|
||||
|
||||
if(!(std::abs(gain) > GainSilenceThreshold))
|
||||
return;
|
||||
if(size_t todo{(InSamples.size()-pos) >> 2})
|
||||
{
|
||||
const __m128 gain4{_mm_set1_ps(gain)};
|
||||
do {
|
||||
const __m128 val4{_mm_load_ps(&InSamples[pos])};
|
||||
__m128 dry4{_mm_load_ps(&dst[pos])};
|
||||
dry4 = _mm_add_ps(dry4, _mm_mul_ps(val4, gain4));
|
||||
_mm_store_ps(&dst[pos], dry4);
|
||||
pos += 4;
|
||||
} while(--todo);
|
||||
}
|
||||
for(size_t leftover{(InSamples.size()-pos)&3};leftover;++pos,--leftover)
|
||||
dst[pos] += InSamples[pos] * gain;
|
||||
}
|
||||
|
@ -84,6 +84,19 @@ inline MixerOutFunc SelectMixer()
|
||||
return Mix_<CTag>;
|
||||
}
|
||||
|
||||
inline MixerOneFunc SelectMixerOne()
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
if((CPUCapFlags&CPU_CAP_NEON))
|
||||
return Mix_<NEONTag>;
|
||||
#endif
|
||||
#ifdef HAVE_SSE
|
||||
if((CPUCapFlags&CPU_CAP_SSE))
|
||||
return Mix_<SSETag>;
|
||||
#endif
|
||||
return Mix_<CTag>;
|
||||
}
|
||||
|
||||
inline HrtfMixerFunc SelectHrtfMixer()
|
||||
{
|
||||
#ifdef HAVE_NEON
|
||||
@ -153,6 +166,7 @@ void Voice::InitMixer(al::optional<std::string> resampler)
|
||||
}
|
||||
|
||||
MixSamplesOut = SelectMixer();
|
||||
MixSamplesOne = SelectMixerOne();
|
||||
MixHrtfBlendSamples = SelectHrtfBlendMixer();
|
||||
MixHrtfSamples = SelectHrtfMixer();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user