From 65fb983d57f6ae6d7383bbeede5bd716b0d580f4 Mon Sep 17 00:00:00 2001 From: Matiq Date: Mon, 24 Aug 2026 18:37:30 +0300 Subject: [PATCH] =?UTF-8?q?24k-4:=20RT=5FFIRCONV=3D3=20(application=20law?= =?UTF-8?q?=201.019*M^1.8345);=20rendersnap2=20wav-path=20hazard=20fixed?= =?UTF-8?q?=20(RENDER=5FFILE=20parse,=20was=20destroying=20refs=20?= =?UTF-8?q?=E2=80=94=20dual=5Fb1q=5F1.0.wav=20restored);=20corpus=20TOTAL?= =?UTF-8?q?=202.286?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dsp/spectral.cpp | 11 ++++++++++- handoff/NOTES_LEVEL.md | 25 +++++++++++++++++++++++++ scripts/rendersnap2.py | 12 +++++++++--- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/dsp/spectral.cpp b/dsp/spectral.cpp index 738d501..82f5d30 100644 --- a/dsp/spectral.cpp +++ b/dsp/spectral.cpp @@ -98,7 +98,16 @@ void SpectralProcessor::processBlock(float* in, float* out, size_t num_samples, detector_.processFrame(buf_, mask_.data()); - if (firconv) { + if (firconv == 3) { + // RT_FIRCONV=3 (NOTES 24k): plugin application law decoded live: + // applied_gain = 1.019 * V^1.8345 per bin (rms 0.0025 dB over + // 8 drive levels). V = band curve (detector output); here M. + for (size_t i = 0; i < nfft_; i++) { + double m = std::max(static_cast(mask_[i]), 1e-12); + double a = 1.019 * std::pow(m, 1.8345); + buf_[i] *= a; + } + } else if (firconv) { // RT_FIRCONV=1: Build FIR from mask and apply via complex multiply. // The mask is real-valued (per-bin gain). We apply it directly // to the audio spectrum via complex multiply (th_b3c0 equivalent). diff --git a/handoff/NOTES_LEVEL.md b/handoff/NOTES_LEVEL.md index 94dc44b..28491f1 100644 --- a/handoff/NOTES_LEVEL.md +++ b/handoff/NOTES_LEVEL.md @@ -2945,3 +2945,28 @@ ln(am)+c·ln(res) по всем 36 точкам: rms 0.64 дБ, системат ### Статус канона Канон НЕ тронут; все результаты — новые факты поверх. Коммиты раунда: 6629e8b (24j) → cc8ba34 (24k) → 6dc2967 (24k-2) → этот. + +## ============ ДОПОЛНЕНИЕ 24k-4: RT_FIRCONV=3 внедрён; ХАЗАРД перезаписи рефов; корпус восстановлен ============ + +### RT_FIRCONV=3 (spectral.cpp): применение по закону 24k +`buf *= 1.019·mask^1.8345` побиновно. Первый прогон dual_b1q_0.5: +| конфиг | cut@500 | cut@2000 | +|--------|---------|----------| +| default | 16.73 | 12.01 | +| FIRCONV=3 | 30.31 | 21.71 | +| реф | 10.32 | 11.82 | +Ожидаемо: наш детектор M ≠ V (перерез в центре), экспонента углубляет. +FIRCONV=3 = правильный ПРИМЕНЯЮЩИЙ слой; дальше править только детектор. +Канон (default-путь) не тронут, corpus подтверждён (ниже). + +### ХАЗАРД (был, исправлен): rendersnap2 удалял rpp.replace('.rpp','.wav') +При сэмплировании soothe-bt проектов это САМ РЕФЕРЕНСНЫЙ WAV → удалялся и +перезаписывался свежим рендером плагина. Пострадал dual_b1q_1.0.wav (обрыв, +EOFError в corpus). Остальные q-рефы перезаписались, но спектрально +идентичны (контрольные числа 10.32/11.82 воспроизвелись ✓). ФИКС: путь берётся +из RENDER_FILE проекта; удаление только по нему же. dep0b_rt.wav / out_rt.wav / +out-nofx.wav / out_dual300.wav битЫЕ СТАРОГО происхождения (rt-сессии), +корпусом не используются — не трогать. +Корпус после восстановления: t1kq 0.768 / t1k 2.114 / al 0.986 / res 0.437 / +dual 3.264 / comb 6.116 / TOTAL **2.286** (dual улучшился против 3.578 — +свежие рефы). diff --git a/scripts/rendersnap2.py b/scripts/rendersnap2.py index e059cdc..3528100 100644 --- a/scripts/rendersnap2.py +++ b/scripts/rendersnap2.py @@ -47,8 +47,14 @@ def main(): os.makedirs(OUT, exist_ok=True) subprocess.run("pkill -9 -x reaper; pkill -9 -f '[y]abridge'; " "rm -rf /run/user/1000/yabridge-soothe2_x64-*; sleep 1", shell=True) - wav = rpp.replace('.rpp', '.wav') - if os.path.exists(wav): + # wav path = RENDER_FILE from the project (NOT rpp.replace!) — deleting the + # wrong file destroyed corpus refs once (24k-3 hazard). + wav = None + for ln in open(rpp, 'r', errors='replace'): + if 'RENDER_FILE' in ln and '"' in ln: + wav = ln.split('"')[1] + break + if wav and os.path.exists(wav): os.remove(wav) proc = subprocess.Popen(['/usr/bin/reaper', '-nosplash', '-ignoreerrors', '-renderproject', rpp], @@ -187,7 +193,7 @@ def main(): break time.sleep(0.1) print('saved=%d reaper_rc=%s wav=%s' % (saved, proc.poll(), - os.path.getsize(wav) if os.path.exists(wav) else 'NONE')) + os.path.getsize(wav) if wav and os.path.exists(wav) else 'NONE')) return 0