| 240 | } // namespace |
| 241 | |
| 242 | void ClientReverb::process(float* interleaved, int frames, int channels) noexcept |
| 243 | { |
| 244 | if (frames <= 0) return; |
| 245 | if (channels != 1 && channels != 2) return; |
| 246 | |
| 247 | recacheIfDirty(); |
| 248 | const bool enabled = m_atomics.enabled.load(std::memory_order_acquire); |
| 249 | |
| 250 | float inPeakLin = 0.0f; |
| 251 | float outPeakLin = 0.0f; |
| 252 | float wetSumSq = 0.0f; |
| 253 | |
| 254 | const float dryGain = m_cached.dryGain; |
| 255 | const float wetGain = m_cached.wetGain; |
| 256 | const float damp1 = m_cached.damp1; |
| 257 | const float damp2 = m_cached.damp2; |
| 258 | const int preDelayN = m_cached.preDelaySamples; |
| 259 | |
| 260 | for (int f = 0; f < frames; ++f) { |
| 261 | const float inL = interleaved[f * channels]; |
| 262 | const float inR = (channels == 2) ? interleaved[f * channels + 1] : inL; |
| 263 | |
| 264 | const float inAbs = std::max(std::fabs(inL), std::fabs(inR)); |
| 265 | if (inAbs > inPeakLin) inPeakLin = inAbs; |
| 266 | |
| 267 | if (!enabled) { |
| 268 | // Bypass path — leave sample untouched. |
| 269 | const float oAbs = std::max(std::fabs(inL), std::fabs(inR)); |
| 270 | if (oAbs > outPeakLin) outPeakLin = oAbs; |
| 271 | continue; |
| 272 | } |
| 273 | |
| 274 | // Pre-delay stage. Write raw input to ring buffer; read the |
| 275 | // sample that entered preDelayN frames ago. |
| 276 | m_preDelay[m_preDelayWrite * 2] = inL; |
| 277 | m_preDelay[m_preDelayWrite * 2 + 1] = inR; |
| 278 | int readIdx = m_preDelayWrite - preDelayN; |
| 279 | if (readIdx < 0) readIdx += m_preDelayCap; |
| 280 | const float delL = m_preDelay[readIdx * 2]; |
| 281 | const float delR = m_preDelay[readIdx * 2 + 1]; |
| 282 | m_preDelayWrite = (m_preDelayWrite + 1) % m_preDelayCap; |
| 283 | |
| 284 | // Feed reverb with attenuated delayed signal. |
| 285 | const float inputL = delL * kFixedGain; |
| 286 | const float inputR = delR * kFixedGain; |
| 287 | |
| 288 | // 8 parallel combs, summed per channel. |
| 289 | float wetL = 0.0f; |
| 290 | float wetR = 0.0f; |
| 291 | for (int i = 0; i < kNumCombs; ++i) { |
| 292 | wetL += processCombImpl(m_chL.combs[i], inputL, |
| 293 | m_cached.combFeedback[i], damp1, damp2, |
| 294 | m_cached.combLen[i]); |
| 295 | wetR += processCombImpl(m_chR.combs[i], inputR, |
| 296 | m_cached.combFeedback[i], damp1, damp2, |
| 297 | m_cached.combLen[i]); |
| 298 | } |
| 299 |
nothing calls this directly
no test coverage detected