Stereo linking: max(|L|,|R|) drives envelope, and the same gain is applied to both. Feed L-only signal and check R remains silent (no cross-channel leakage) but the L channel is still compressed.
| 275 | // applied to both. Feed L-only signal and check R remains silent (no |
| 276 | // cross-channel leakage) but the L channel is still compressed. |
| 277 | void testStereoLinked() |
| 278 | { |
| 279 | ClientComp comp; |
| 280 | comp.prepare(kSampleRate); |
| 281 | comp.setEnabled(true); |
| 282 | comp.setThresholdDb(-20.0f); |
| 283 | comp.setRatio(4.0f); |
| 284 | comp.setKneeDb(0.0f); |
| 285 | comp.setMakeupDb(0.0f); |
| 286 | comp.setLimiterEnabled(false); |
| 287 | |
| 288 | const int frames = static_cast<int>(kSampleRate); |
| 289 | std::vector<float> buf(frames * 2, 0.0f); |
| 290 | const float amp = 0.5f; |
| 291 | const double twoPi = 6.283185307179586476; |
| 292 | for (int i = 0; i < frames; ++i) { |
| 293 | buf[i * 2] = amp * static_cast<float>( |
| 294 | std::sin(twoPi * 1000.0 * i / kSampleRate)); |
| 295 | buf[i * 2 + 1] = 0.0f; // R silent |
| 296 | } |
| 297 | |
| 298 | // Warmup |
| 299 | std::vector<float> warm(frames * 2, 0.0f); |
| 300 | for (int i = 0; i < frames; ++i) { |
| 301 | warm[i * 2] = amp * static_cast<float>( |
| 302 | std::sin(twoPi * 1000.0 * i / kSampleRate)); |
| 303 | } |
| 304 | processBlocks(comp, warm.data(), frames); |
| 305 | |
| 306 | processBlocks(comp, buf.data(), frames); |
| 307 | |
| 308 | // R channel should stay exactly zero (no cross-talk). |
| 309 | float rPeak = 0.0f; |
| 310 | for (int i = 0; i < frames; ++i) { |
| 311 | rPeak = std::max(rPeak, std::fabs(buf[i * 2 + 1])); |
| 312 | } |
| 313 | // L channel should show compression (< input amplitude). |
| 314 | float lPeak = 0.0f; |
| 315 | for (int i = 0; i < frames; ++i) { |
| 316 | lPeak = std::max(lPeak, std::fabs(buf[i * 2])); |
| 317 | } |
| 318 | |
| 319 | report("stereo-linked: R stays silent when only L is active", |
| 320 | rPeak < 1e-6f, |
| 321 | "R peak=" + std::to_string(rPeak)); |
| 322 | report("stereo-linked: L channel still compressed", |
| 323 | lPeak < amp * 0.95f, |
| 324 | "L peak=" + std::to_string(lPeak) |
| 325 | + " (input=" + std::to_string(amp) + ")"); |
| 326 | } |
| 327 | |
| 328 | // Attack timing: envelope should reach ~63 % of step in τ ms. |
| 329 | // Drive silence → 0.5 amp step, sample GR shortly after the step. |
no test coverage detected