Moving the frequency knob should steer the sidechain band: a 2 kHz tone that triggered attenuation at freq=2000 should be untouched at freq=10000.
| 204 | // 2 kHz tone that triggered attenuation at freq=2000 should be |
| 205 | // untouched at freq=10000. |
| 206 | void testFrequencySteering() |
| 207 | { |
| 208 | ClientDeEss d; |
| 209 | d.prepare(kSampleRate); |
| 210 | d.setEnabled(true); |
| 211 | d.setQ(2.0f); |
| 212 | d.setThresholdDb(-40.0f); |
| 213 | d.setAmountDb(-12.0f); |
| 214 | d.setAttackMs(0.5f); |
| 215 | d.setReleaseMs(50.0f); |
| 216 | |
| 217 | const float amp = dbToLin(-6.0f); |
| 218 | |
| 219 | // Tuned to 2 kHz — a 2 kHz tone should trigger attenuation. |
| 220 | d.setFrequencyHz(2000.0f); |
| 221 | d.reset(); |
| 222 | auto a = makeTone(2000.0, 12000, amp); |
| 223 | processBlocks(d, a.data(), 12000); |
| 224 | auto aTail = makeTone(2000.0, 512, amp); |
| 225 | processBlocks(d, aTail.data(), 512); |
| 226 | const float grLow = linToDb(peakAbsStereo(aTail.data(), 512) / amp); |
| 227 | |
| 228 | // Move the sidechain up to 10 kHz; same 2 kHz tone should now |
| 229 | // slip past it. |
| 230 | d.setFrequencyHz(10000.0f); |
| 231 | d.reset(); |
| 232 | auto b = makeTone(2000.0, 12000, amp); |
| 233 | processBlocks(d, b.data(), 12000); |
| 234 | auto bTail = makeTone(2000.0, 512, amp); |
| 235 | processBlocks(d, bTail.data(), 512); |
| 236 | const float grHigh = linToDb(peakAbsStereo(bTail.data(), 512) / amp); |
| 237 | |
| 238 | // With a broad Q=2 bandpass at 10 kHz the 2 kHz tone still leaks |
| 239 | // some reduction (skirt rolloff, not brick-wall). The point is |
| 240 | // that steering produces a significantly different gate response |
| 241 | // — require at least 5 dB more reduction on the in-tune side. |
| 242 | report("frequency: steering changes what counts as sibilant", |
| 243 | grLow < grHigh - 5.0f, |
| 244 | "grLow=" + std::to_string(grLow) |
| 245 | + " grHigh=" + std::to_string(grHigh)); |
| 246 | } |
| 247 | |
| 248 | // Sanity: complex tone with AM modulation produces finite output, no |
| 249 | // NaN/Inf, peak doesn't exceed input. |
no test coverage detected