1 #ifndef CORE_MIXER_HRTFBASE_H
2 #define CORE_MIXER_HRTFBASE_H
9 #include "opthelpers.h"
12 using uint
= unsigned int;
14 using ApplyCoeffsT
= void(const al::span
<float2
> Values
, const size_t irSize
,
15 const ConstHrirSpan Coeffs
, const float left
, const float right
);
17 template<ApplyCoeffsT ApplyCoeffs
>
18 inline void MixHrtfBase(const al::span
<const float> InSamples
, const al::span
<float2
> AccumSamples
,
19 const size_t IrSize
, const MixHrtfFilter
*hrtfparams
, const size_t SamplesToDo
)
21 ASSUME(SamplesToDo
> 0);
22 ASSUME(SamplesToDo
<= BufferLineSize
);
23 ASSUME(IrSize
<= HrirLength
);
25 const ConstHrirSpan Coeffs
{hrtfparams
->Coeffs
};
26 const float gainstep
{hrtfparams
->GainStep
};
27 const float gain
{hrtfparams
->Gain
};
29 size_t ldelay
{HrtfHistoryLength
- hrtfparams
->Delay
[0]};
30 size_t rdelay
{HrtfHistoryLength
- hrtfparams
->Delay
[1]};
31 float stepcount
{0.0f
};
32 for(size_t i
{0u};i
< SamplesToDo
;++i
)
34 const float g
{gain
+ gainstep
*stepcount
};
35 const float left
{InSamples
[ldelay
++] * g
};
36 const float right
{InSamples
[rdelay
++] * g
};
37 ApplyCoeffs(AccumSamples
.subspan(i
), IrSize
, Coeffs
, left
, right
);
43 template<ApplyCoeffsT ApplyCoeffs
>
44 inline void MixHrtfBlendBase(const al::span
<const float> InSamples
,
45 const al::span
<float2
> AccumSamples
, const size_t IrSize
, const HrtfFilter
*oldparams
,
46 const MixHrtfFilter
*newparams
, const size_t SamplesToDo
)
48 ASSUME(SamplesToDo
> 0);
49 ASSUME(SamplesToDo
<= BufferLineSize
);
50 ASSUME(IrSize
<= HrirLength
);
52 const ConstHrirSpan OldCoeffs
{oldparams
->Coeffs
};
53 const float oldGainStep
{oldparams
->Gain
/ static_cast<float>(SamplesToDo
)};
54 const ConstHrirSpan NewCoeffs
{newparams
->Coeffs
};
55 const float newGainStep
{newparams
->GainStep
};
57 if(oldparams
->Gain
> GainSilenceThreshold
) LIKELY
59 size_t ldelay
{HrtfHistoryLength
- oldparams
->Delay
[0]};
60 size_t rdelay
{HrtfHistoryLength
- oldparams
->Delay
[1]};
61 auto stepcount
= static_cast<float>(SamplesToDo
);
62 for(size_t i
{0u};i
< SamplesToDo
;++i
)
64 const float g
{oldGainStep
*stepcount
};
65 const float left
{InSamples
[ldelay
++] * g
};
66 const float right
{InSamples
[rdelay
++] * g
};
67 ApplyCoeffs(AccumSamples
.subspan(i
), IrSize
, OldCoeffs
, left
, right
);
73 if(newGainStep
*static_cast<float>(SamplesToDo
) > GainSilenceThreshold
) LIKELY
75 size_t ldelay
{HrtfHistoryLength
+1 - newparams
->Delay
[0]};
76 size_t rdelay
{HrtfHistoryLength
+1 - newparams
->Delay
[1]};
77 float stepcount
{1.0f
};
78 for(size_t i
{1u};i
< SamplesToDo
;++i
)
80 const float g
{newGainStep
*stepcount
};
81 const float left
{InSamples
[ldelay
++] * g
};
82 const float right
{InSamples
[rdelay
++] * g
};
83 ApplyCoeffs(AccumSamples
.subspan(i
), IrSize
, NewCoeffs
, left
, right
);
90 template<ApplyCoeffsT ApplyCoeffs
>
91 inline void MixDirectHrtfBase(const FloatBufferSpan LeftOut
, const FloatBufferSpan RightOut
,
92 const al::span
<const FloatBufferLine
> InSamples
, const al::span
<float2
> AccumSamples
,
93 const al::span
<float,BufferLineSize
> TempBuf
, const al::span
<HrtfChannelState
> ChannelState
,
94 const size_t IrSize
, const size_t SamplesToDo
)
96 ASSUME(SamplesToDo
> 0);
97 ASSUME(SamplesToDo
<= BufferLineSize
);
98 ASSUME(IrSize
<= HrirLength
);
99 assert(ChannelState
.size() == InSamples
.size());
101 auto ChanState
= ChannelState
.begin();
102 for(const FloatBufferLine
&input
: InSamples
)
104 /* For dual-band processing, the signal needs extra scaling applied to
105 * the high frequency response. The band-splitter applies this scaling
106 * with a consistent phase shift regardless of the scale amount.
108 ChanState
->mSplitter
.processHfScale(al::span
{input
}.first(SamplesToDo
), TempBuf
,
109 ChanState
->mHfScale
);
111 /* Now apply the HRIR coefficients to this channel. */
112 const ConstHrirSpan Coeffs
{ChanState
->mCoeffs
};
113 for(size_t i
{0u};i
< SamplesToDo
;++i
)
115 const float insample
{TempBuf
[i
]};
116 ApplyCoeffs(AccumSamples
.subspan(i
), IrSize
, Coeffs
, insample
, insample
);
122 /* Add the HRTF signal to the existing "direct" signal. */
123 const auto left
= al::span
{al::assume_aligned
<16>(LeftOut
.data()), SamplesToDo
};
124 std::transform(left
.cbegin(), left
.cend(), AccumSamples
.cbegin(), left
.begin(),
125 [](const float sample
, const float2
&accum
) noexcept
-> float
126 { return sample
+ accum
[0]; });
127 const auto right
= al::span
{al::assume_aligned
<16>(RightOut
.data()), SamplesToDo
};
128 std::transform(right
.cbegin(), right
.cend(), AccumSamples
.cbegin(), right
.begin(),
129 [](const float sample
, const float2
&accum
) noexcept
-> float
130 { return sample
+ accum
[1]; });
132 /* Copy the new in-progress accumulation values to the front and clear the
133 * following samples for the next mix.
135 const auto accum_inprog
= AccumSamples
.subspan(SamplesToDo
, HrirLength
);
136 auto accum_iter
= std::copy(accum_inprog
.cbegin(), accum_inprog
.cend(), AccumSamples
.begin());
137 std::fill_n(accum_iter
, SamplesToDo
, float2
{});
140 #endif /* CORE_MIXER_HRTFBASE_H */