← все проверки

TuneTON §13 — качество DSP

Фаза 0.A · доказательство варианта A из ADR-0020
Это ВЕБ-цепочка, целиком внутри WebView. Companion здесь не участвует ни в каком виде: премиум-эффектов (сатуратор, биткрашер) на этой странице нет физически, кнопок на локскрине не будет, звук идёт из браузера. Всё, что касается Companion, — на странице dashboard.html, и там обязателен зелёный индикатор.
Зачем эта страница. Device-прогон 0.A показал CPU-запас 17× и детерминизм corr 1.0, но звук на слух плохой. ADR-0020 фиксирует: причина — реализация DSP, а не предел стека, и предлагает вариант A — остаться на едином Faust-ядре (паритет web↔Companion, ADR-0015), а качество решить инженерно. Эта страница сравнивает V1 (текущая цепочка) и V2 (редизайн) и измеряет разницу — «на слух» для приёмки ADR-0020 явно объявил недостаточным.
Что изменено в V2 (3 эффекта из ADR-0020):
1. Дисторшн → ADAA1. В Faust один sample rate, поэтому истинный оверсэмплинг внутри process невыразим (это важное ограничение, ADR-0020 предполагал оверсэмплинг). Вместо него — aa.tanh1: антиалиасинг методом антипроизводной 1-го порядка. Давит алиасинг на самой нелинейности и снимает необходимость в post-LPF 12 кГц, которым V1 глушила верх.
2. Lo-fi → анти-image + TPDF-дизер. V1 ставила LPF только ПЕРЕД децимацией. Но sample-and-hold — это ZOH, он порождает образы выше новой частоты Найквиста; именно они и есть «писк», и pre-LPF их не убирает. V2 добавляет post-LPF после квантования и TPDF-дизер (декоррелирует ошибку квантования → окраска вместо «цифровой грязи»).
3. Реверб → re.zita_rev1_stereo. V1 — два независимых mono_freeverb, псевдо-стерео. V2 — настоящий стерео-ревербератор с pre-delay 20 мс, раздельным T60 по НЧ/СЧ и equal-power wet/dry (V1 линейно микшировала → провал громкости на середине регулятора).
Детерминизм не нарушен. no.noise в Faust — LCG с фиксированным seed 12345, сбрасываемым на init, так что дизер воспроизводим. Ползунок dither = 0 даёт бит-точный режим для §11, если понадобится.
Паритет — проверить при переносе в Companion. aa.tanh1 делит на разность отсчётов, и библиотека требует double. Web-цепочка компилируется с -double; сборка NDK в Companion должна использовать ту же точность, иначе ADR-0015 (паритет web↔native) поедет. Фактические флаги компиляции попадают в отчёт ниже.

§13.0 Компиляция

не запускалось
Каждый DSP компилируется независимо и ловит свою ошибку — неверная сигнатура в одном эффекте не роняет остальные измерения. Компиляция идёт на устройстве (@grame/faustwasm@0.16.3), первый вызов заметно дольше.

§13.1 Алиасинг и THD дисторшна

Главное число этой страницы. Синус подаётся в изолированную нелинейность, выход раскладывается FFT (32768, окно Ханна). Бины делятся на три группы: основной тон, гармоники k·f0 и всё остальное — это и есть алиасинг (продукты, отражённые от Найквиста).

Тоны 5000 и 7000 Гц выбраны так, что при 48 кГц ни один отражённый продукт не попадает на кратную f0 частоту — иначе алиасинг был бы неотличим от гармоники (по этой причине 1000 Гц для такого теста непригоден).

Критерий приёмки: aliasToSignalDb у V2 должен быть заметно ниже V1. Разница в 10–20 дБ — это и есть инженерное доказательство варианта A.

Результат прогона 2026-08-01 (Pixel 4a): критерий НЕ выполнен. 5 кГц −0.74 дБ (V2 хуже), 7 кГц +0.96 дБ — вместо 10–20 дБ. Абсолютный пол алиасинга ~−43 дБ у обеих версий. Улучшилось только отношение alias/гармоники на 7 кГц (+8.9 → −15.85 дБ), но из-за роста гармоник после снятия post-LPF 12 кГц, а не из-за падения алиасинга. Вывод: ADAA1 не заменяет оверсэмплинг — истинный оверсэмплинг доступен только нативно (ADR-0022/0023), и замена в ADR-0020 требует пересмотра.
не запускалось

§13.2 АЧХ полной цепочки

Логарифмический свип 20 Гц → 20 кГц рендерится дважды: без обработки (референс) и через цепочку. Отношение спектров по третьоктавным полосам даёт АЧХ. Показывает, сколько верха съедает lo-fi и что реально делает EQ. Метод приблизительный (цепочка нелинейна, энергия перераспределяется), годится для «где завал», не для сертификации.
не запускалось

§13.3 Реверб: T60, стерео, Faust vs нативная свёртка

Импульс через ревербератор, обратное интегрирование Шрёдера по кривой затухания → оценка T60 (по наклону −5…−25 дБ). Корреляция L/R по хвосту: у настоящего стерео она заметно ниже 1, у псевдо-стерео близка к 1.

Третьим прогоняется нативный ConvolverNode с синтезированным IR (экспоненциально затухающий декоррелированный шум) — это прямой A/B «Faust-реверб vs нативная свёртка», который ADR-0020 назвал условием выбора между A и C.

Исправлено после прогона 2026-08-01. Тогда t60Sec вышел null у обеих Faust-версий, а нативная свёртка померилась штатно — то есть ломалось измерение, а не ревербы. Причина: импульс подавался в нулевой отсчёт, когда revMix ещё не разогнался через si.smoo, поэтому сквозь микс проходил сухой импульс и забивал интеграл Шрёдера. Теперь меряется чистый wet (отдельные DSP без сухого пути и без слайдера микса), импульс сдвинут на 0.25 с, а рядом считается tailRmsDb — он отличает «хвоста нет» (−100 дБ и ниже) от «измерение не сошлось».
не запускалось

§13.4 Цена качества по CPU

Тот же оффлайн-прокси, что в §10.2 (renderSpeed = длительность / время рендера). ADAA и zita дороже наивных версий — надо убедиться, что запас 17× не съеден. Ниже 3–5× появляется риск glitch на слабом CPU.
не запускалось

§13.5 Слепое прослушивание

Переключение без перезапуска трека — обе цепочки собраны заранее и работают параллельно, звучит только выбранная. Слушать: «писк» lo-fi, грязь дисторшна, объём и хвост реверба, провал громкости при среднем reverbMix.
Три ручки выше есть только у V2 — ими и ищется рабочая точка lo-fi. Меньше бит = грязнее и характернее; анти-image на 0 оставляет ZOH-образы (тот самый «писк»), на 1 давит их вместе с характером. Найденные значения потом переносятся в companion-poc/effect.dsp, а не остаются на этой странице.
не запускалось