MCPcopy Create free account
hub / github.com/aethersdr/AetherSDR / process

Method process

src/core/ClientComp.cpp:233–344  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

231}
232
233void ClientComp::process(float* interleaved, int frames, int channels) noexcept
234{
235 if (frames <= 0) return;
236 if (channels != 1 && channels != 2) return;
237
238 recacheIfDirty();
239 const bool enabled = m_atomics.enabled.load(std::memory_order_acquire);
240
241 // Pre-comp PAPR shaping (#2887). The rotator runs first to
242 // symmetrize asymmetric voice peaks; the drive gain then pushes
243 // more material across the threshold so the existing comp curve
244 // engages harder and the brickwall limiter below contains the
245 // resulting hot peaks. These run independent of the comp's enabled
246 // flag — they're a useful pair even when the comp curve itself is
247 // bypassed (Drive + Limiter alone is the simplest broadcast PAPR
248 // setup). Their work happens *before* the input peak meter so the
249 // GR readout reflects the comp's workload at the boosted level.
250 if (m_cached.phaseRotatorStages > 0 && m_phaseRotator) {
251 m_phaseRotator->process(interleaved, frames, channels);
252 }
253 if (m_cached.driveLin != 1.0f) {
254 const float gain = m_cached.driveLin;
255 const int n = frames * channels;
256 for (int i = 0; i < n; ++i) interleaved[i] *= gain;
257 }
258
259 float inPeakLin = 0.0f;
260 float outPeakLin = 0.0f;
261 float worstGrDb = 0.0f; // most negative (largest reduction)
262 float worstLimGrDb = 0.0f; // limiter-only GR, most negative
263 bool limFired = false;
264
265 const float attackCoeff = m_cached.attackCoeff;
266 const float releaseCoeff = m_cached.releaseCoeff;
267 const float makeup = m_cached.makeupLin;
268 const float limCeiling = m_cached.limCeilingLin;
269 const float limAttack = m_cached.limAttackCoeff;
270 const float limRelease = m_cached.limReleaseCoeff;
271
272 for (int f = 0; f < frames; ++f) {
273 float l = interleaved[f * channels];
274 float r = (channels == 2) ? interleaved[f * channels + 1] : l;
275
276 const float inAbs = std::max(std::fabs(l), std::fabs(r));
277 if (inAbs > inPeakLin) inPeakLin = inAbs;
278
279 // Compressor gain (only applied if enabled). Linear-domain peak
280 // envelope: smoothed |x| with asymmetric attack/release, converted
281 // to dB once to feed the static curve. This tracks the actual
282 // peak amplitude of the signal (unlike a dB-domain filter that
283 // would average log|x| and read ~4 dB below peak on a sine).
284 float gainLin = 1.0f;
285 if (enabled) {
286 const float alpha = (inAbs > m_envLin) ? attackCoeff : releaseCoeff;
287 m_envLin += alpha * (inAbs - m_envLin);
288 const float envDb = linToDb(std::max(m_envLin, 1e-6f));
289 const float gainDb = staticCurveGainDb(envDb);
290 if (gainDb < worstGrDb) worstGrDb = gainDb;

Callers

nothing calls this directly

Calls 3

linToDbFunction · 0.70
dbToLinFunction · 0.70
loadMethod · 0.45

Tested by

no test coverage detected