diff --git a/dsp/framed_model.cpp b/dsp/framed_model.cpp index a44e37c..8559e68 100644 --- a/dsp/framed_model.cpp +++ b/dsp/framed_model.cpp @@ -357,12 +357,24 @@ void FramedDetector::processFrame(const std::complex* spectrum, float* m double att = std::exp(-1.0 * (nfft_ / 4) / (tatt * sample_rate_)); double rel = std::exp(-1.0 * (nfft_ / 4) / (trel * sample_rate_)); + // RT_ENV=live (NOTES 22n): detector envelope from live tables kRTAtt/kRTRel — + // attack as feed-forward, release as retention (~tau 2s at hop rate). This is + // the slow adaptation the real plugin exhibits on sustained content. + static const int env_live = getenv("RT_ENV") ? atoi(getenv("RT_ENV")) : 0; + for (size_t k = 0; k <= half; k++) { double a_cur = 2.0 * std::abs(spectrum[k]) / wsum_; - double am = am_[k]; - if (a_cur > am) am = att * am + (1.0 - att) * a_cur; - else am = rel * am + (1.0 - rel) * a_cur; - am_[k] = static_cast(am); + if (env_live) { + double d = a_cur - static_cast(am_[k]); + if (d > 0) am_[k] = static_cast(am_[k] + d * static_cast(kRTAtt[k])); + else am_[k] = static_cast(static_cast(kRTRel[k]) * am_[k] + + (1.0 - static_cast(kRTRel[k])) * a_cur); + } else { + double am = am_[k]; + if (a_cur > am) am = att * am + (1.0 - att) * a_cur; + else am = rel * am + (1.0 - rel) * a_cur; + am_[k] = static_cast(am); + } } for (size_t k = 0; k <= half; k++) mask[k] = 1.0f; diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index d565bf1..e968ebb 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -1833,3 +1833,31 @@ kRTRel≈0.96..1.0 retention на кадр (hop 1024@48k → τ≈2с)! 2. Откалибровать att/rel семантику по переходным кривым (short-tone транзиент −10.3→−20.7 за ~секунды — идеальный тестовый сигнал для фита kRTAtt/kRTRel). 3. Перечитать mangled scale-блок декомпа с живыми 1a0=4096/870=1.0. + +## ============ UPDATE 2026-08-22o: RT_ENV=live + корпусные прогоны новой архитектуры ============ + +RT_ENV=live (детекторный энвелоп из kRTAtt/kRTRel: атака feed-forward, релиз retention +~tau2s) добавлен в FramedDetector::processFrame за флагом. +- Белый шум: mean|err| улучшился 0.632 → **0.461** ✓ +- Корпус: НЕЙТРАЛЕН (метрика меряет сошедшиеся окна — энвелоп сходится и там). + +Корпусные варианты новой архитектуры (все с RT_LUT_OFF/IIR12=0/NOWARP/NOBLEND/NOIIR3/FLOOR): +| конфиг | TOTAL | примечание | +|--------|-------|------------| +| pool=5 scale=1.144 | 5.905 | тон точен, шум-центр перерезан | +| pool=0 scale=1.144 | **5.386** | t1kq 5.574→3.621, res хуже 1.713 | +| канон (без env) | 2.286 | HEAD, не тронут | + +Вывод: архитектурные находки верны на пробниках (тон-пол точен, шум плоский), но +константы (scale 1.144 снят с одного случая) не обобщаются на корпус ⇒ нужен +систематический joint-фит (pool_w × scale × cap-law) минимизацией TOTAL, плюс +поиск источника масштаба в декомпе (золотое правило). Канон остаётся HEAD до +превышения гейта. + +### NEXT (приоритеты) +1. Joint-фит сеткой: pool_w {0,3,5} x scale {1.05,1.10,1.144,1.20} x cap{on,off} + (~12 прогонов корпуса по ~2 мин). +2. Декомп амплитудной нормировки: wsum/am (spectral.cpp vs детекторный путь), + источник scale (int@1a0=4096, f32@870=1.0 живые!). +3. Транзиентный калибратор: короткий тон некорректен из-за латенси/WOLA коротких + рендеров — заменить на длинный файл со СТУПЕНЬЮ уровня внутри.