From 36d42933108104519da62cbd7373d5129bbc680f Mon Sep 17 00:00:00 2001 From: Zeyu Yang <40936154+zeyuyang42@users.noreply.github.com> Date: Fri, 16 Oct 2026 21:19:54 +0200 Subject: [PATCH] core: crest factor outputs 0 on silence instead of NaN peak / RMS with both at 0 is NaN, and the NaN rode into every object downstream once the input went quiet. Report 0 like the spectral features do with no energy. std::fabs instead of abs on a double. Max: zerr.features~ cf no longer poisons zerr.envelopes~ on silence. Pd: zerr_features~ cf, same. Notes added to max-v0.3.0.md and pd-next.md. Co-Authored-By: Claude Fable 5.1 --- core/src/features/crestfactor.cpp | 6 ++++-- docs/releases/max-v0.3.0.md | 2 ++ docs/releases/pd-next.md | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/features/crestfactor.cpp b/core/src/features/crestfactor.cpp index 48a9d00..8d67ee3 100644 --- a/core/src/features/crestfactor.cpp +++ b/core/src/features/crestfactor.cpp @@ -28,14 +28,16 @@ void CrestFactor::extract() for (int i = 0; i < x_size; ++i) { square_sum += x[i] * x[i]; - peak_tmp = abs(x[i]); + peak_tmp = std::fabs(x[i]); peak_max = peak_tmp > peak_max ? peak_tmp : peak_max; } square_sum = square_sum / x_size; square_root = std::sqrt(square_sum); - crr_y = peak_max / square_root; + // Silence: peak and RMS are both 0 and the ratio would be NaN, which then rides into + // every object downstream. Report 0, like the spectral features do with no energy. + crr_y = square_root > 0.0 ? peak_max / square_root : 0.0; } void CrestFactor::reset() { _reset_param(); } diff --git a/docs/releases/max-v0.3.0.md b/docs/releases/max-v0.3.0.md index a296be3..1963843 100644 --- a/docs/releases/max-v0.3.0.md +++ b/docs/releases/max-v0.3.0.md @@ -43,6 +43,8 @@ First release of Zerr* for Max, as a package for Max 9 on Apple Silicon (arm64, silently on the audio thread. (#30) - `zerr.combinator~` builds the core combinator for real; before #19 the mode argument was parsed and discarded. (#19) +- `zerr.features~ cf` outputs 0 on silence instead of NaN (0/0), which used to poison every + object downstream once the input went quiet. ## Build and tooling diff --git a/docs/releases/pd-next.md b/docs/releases/pd-next.md index ec5e45b..c7067af 100644 --- a/docs/releases/pd-next.md +++ b/docs/releases/pd-next.md @@ -11,3 +11,5 @@ since 2026-10-15 only; everything earlier since `v0.2` still has to be enumerate - `zerr_features~` says why it refuses a block size instead of printing the two numbers: the block must be 1 to 2048 samples, the analysis window, and the sample rate non-zero (#43). The help patch documents the limit. +- `zerr_features~ cf` outputs 0 on silence instead of NaN (0/0), which used to poison every + object downstream once the input went quiet.