Сразу дисклеймер, который я буду повторять ещё раз пять, потому что иначе налетят: модель не восстанавливает утраченные данные, она синтезирует правдоподобную реконструкцию. Это дорисовка в стиле художника-реставратора, а не машина времени. Запомнили? Поехали.


Зачем я вообще во всё это полез

Я люблю музыку. Не “фоном в наушниках”, а так, что жалко каждый трек. Развалившись в кресле или диване, взяв наушники и обмусолив каждую из нескольких тысяч композиций из моего плеера.

Но в последнее время стриминги приучили нас к тому, что музыка - это не вещь, а доступ. А доступ, как выяснилось, штука ооочень хрупкая:

  • Треки в плейлистах сереют

  • Цены растут быстрее ценности

  • Lossless годами был платной привилегией, ну и фейковым, как показала YM

И это я ещё про зарубежный рынок рассказываю. А теперь умножьте на российскую специфику - тут вообще отдельный аттракцион с клоунами и инвалидами:

  • Западные лейблы свернули работу в РФ ещё в 2022-м, и пачка артистов с каталогами просто испарилась из локальных сервисов. Вот был трек, а вот его нет. И это не лицензия протухла, это геополитика подъехала

  • Закон о “пропаганде наркотиков” довёл до того, что Яндекс Музыка, Звук и VK Музыка массово режут и запикивают треки. Куски вырезают задним числом, слова запикивают или заменяют. Пользователи воют, что “пять треков подряд - сплошные запикивания, слушать невозможно”, и это не моя гипербола, а дословная цитата. И прикол тоже с артистами, которые плевали на своих же слушателей и “лишь бы платили бабки”

  • А отдельные треки просто блокируют по требованию РКН

  • Вишенка: Яндекс Музыка поверх всего этого выкатила новый дизайн, который сообщество ласково прозвало “редизайн, который не смог” или хуйнЯ из под конЯ. Половина привычных функций отвалилась, кнопку дизлайка запрятали, кнопки управления скачут по экрану при листании текста. Визуал “сочный”, а пользоваться невозможно.

Люди делают выводы. Винил растёт 18-й год подряд (RIAA), физические носители в мире прибавили за 2025-й (IFPI), у Navidrome 21+ тысяча звёзд на гитхабе, у Jellyfin 53+. А через Bandcamp фанаты занесли артистам $1.74 млрд за музыку, которой владеют.

Я тоже вернулся к собственной коллекции. И тут же воткнулся в проблему, ради которой и написана эта простыня текста

Ваша коллекция из нулевых звучит глухо, и это не банальная вкусовщина, а физика

Если Ваша библиотека, как и моя, собиралась с нулевых - это в основном MP3 128-192 kbps. iTunes Store вообще на запуске в 2003-м продавал треки в 128 kbps, а “высокое качество” 256 kbps подвезли только в 2007-м. Рипы и файлообмен той эпохи - тот же класс, с тех мест же и. сливалось

Lossy-кодек экономит биты двумя путями: огрубляет то, что психоакустическая модель считает незаметным, и тупо отрезает верх спектра. И срезает не наугад: у каждого энкодера это зашитая константа, держите табличку:

Кодек / битрейт

Потолок (lowpass)

MP3 (LAME) 128 kbps

~17.0 кГц

MP3 (LAME) 192 kbps

~18.6 кГц

MP3 (LAME) 320 kbps

~20.5 кГц

AAC (FDK, CBR ≥96k стерео)

до 17.0 кГц

Opus (fullband)

20 кГц

Lossless (44.1 кГц)

22.05 кГц

У энкодеров 90-х (FhG, Xing) на 128 kbps полка была ещё ниже, около 16 кГц. Воздух тарелок, шелест щёток, верхние гармоники вокала - всё, что выше полки, в файле отсутствует

Вернуть эти байты невозможно. Но можно поставить вопрос иначе:

А можно ли синтезировать правдоподобный верхний диапазон, статистически согласованный с тем, что в файле осталось?

Это задача bandwidth extension (BWE) / audio super-resolution, и в ней за последние годы случился настоящий движ: AERO (ICASSP 2023), AudioSR (диффузия, ICASSP 2024), FLowHigh (flow matching, ICASSP 2025) и - ближе всего к моей задаче - Apollo (ICASSP 2025): GAN-модель восстановления сжатой музыки от ребят из Look2Hear, с открытым кодом и весами. Apollo дальше будет нашим спарринг-партнёром, тк по тестам оказался лучшим и самым популярным представителем

И ещё раз, как обещал: модель дорисовывает правдоподобное, а не воскрешает утраченное. Интерфейс инструмента, кстати, напоминает об этом прямым текстом - чтобы никто себе лишнего не придумал.

Что я с Claude в итоге накодил

Да, я использовал ИИ, мне не стыдно или как-то не по себе. Не нравится - закрывай статью, выключай компьютер, выходи на улицу и иди трогать траву

north-star - self-hosted станция для работы с музыкальной коллекцией (код, Apache-2.0 - ТЫК):

  • Библиотека: импорт Ваших папок с сохранением тегов и обложек, браузер по артистам/альбомам/жанрам, веб-плеер;

  • Реставрация: CLI restore.py (папками, рекурсивно, с переносом тегов и обложек, ~15× realtime на M4 Max) и веб-страница с A/B-плеером и спектрограммами «до/после»;

  • Обучение на Вашей коллекции: degrade-пайплайн, тренер с live-метриками прямо в браузере, слепые A/B-тесты, таблица экспериментов - весь ML-цикл наблюдаем;

  • Инфраструктура: FastAPI + Celery + PostgreSQL + MinIO + Redis в докере; ML нативно на Apple Silicon (MPS), работает и на CUDA, и на CPU.

Обзорная панель
Обзорная панель

Дашборд: корпус, эксперименты, чемпион, live-обучение, здоровье сервисов.

Библиотека
Библиотека

Библиотека. На скрине - треки с открытыми лицензиями (CC-BY с archive.org netlabels и public-domain записи Musopen): вся музыка в этой статье легальна для демонстрации, я не идиот.

Принципиальная позиция: всё локально. Файлы не покидают комп, модель учится на Вашей собственной коллекции

Модель, или как я пошёл против SOTA-моды

SOTA-направление в нейроаудио сейчас - комплексные спектрограммы, GAN’ы, диффузия и flow matching: модель предсказывает и магнитуду, и фазу, регенерируя весь спектр целиком. Apollo устроен ровно так: band-split, моделирование последовательности полос, 16.5 млн параметров.

А я сделал противоположную ставку, исходя из специфики именно реставрации lossy-музыки:

Кодек на вменяемом битрейте почти не трогает низ и середину - там пашет психоакустика, и до ~11 кГц сигнал близок к прозрачному. Весь смысл реставрации сидит в верхней полосе. Так зачем регенерировать (и рисковать запороть) 80% спектра, который и так в порядке?

Отсюда и архитектура. Разберём по косточкам, в том числе для самых маленьких в мире нейроаудио.

1. U-Net по лог-магнитуде STFT. Вход - лог-магнитуда L_{\mathrm{in}} = \ln |X_{\mathrm{in}}|, где X = \mathrm{STFT}(x) (окно Ханна 1024, hop 256, 44.1 кГц). 4 уровня, базовая ширина 32 канала, частотное self-attention в боттлнеке, ~8.8 млн параметров - в 1.9 раза меньше Apollo. После основного обучения - adversarial fine-tune с двумя дискриминаторами (multi-period + multi-resolution), который вычищает «мыльность» синтезированного верха.

2. Переиспользование фазы (phase reuse). Модель предсказывает только магнитуду, фаза берётся из входного сигнала:

\hat{x} = \mathrm{ISTFT}\big(\exp(\hat{L}) \cdot e^{i\,\angle X_{\mathrm{in}}}\big)

Для синтезированных частот “правильной” фазы в файле нет, но HF-фаза психоакустически малозначима, а в сохранённой полосе входная фаза - точная

3. Bandwidth gate - реконструкционный приор. Финальная лог-магнитуда собирается так:

\hat{L}(f,t) = \big(1 - w(f)\big)\,L_{\mathrm{in}}(f,t) + w(f)\,\max\big(L_{\mathrm{in}}(f,t),\, L_{\mathrm{pred}}(f,t)\big)w(f) = \mathrm{clip}\!\left(\frac{f - f_0}{\Delta f},\, 0,\, 1\right), \qquad f_0 = 11\,\text{кГц},\ \Delta f = 1.5\,\text{кГц}

Ниже f_0 выход - бит-в-бит вход (кодек сохранил эту полосу лучше, чем её отреставрирует любая нейронка). Выше - \max: расширение полосы строго аддитивно, модель заполняет вырезанное, но не приглушает то, что в файле уже есть.

Обучение: имитация деградации на лету (ffmpeg-роундтрип через MP3/AAC/Opus на случайных битрейтах), L1-лосс по лог-магнитуде с 6-кратным весом полосы >14 кГц, ~1950 lossless-треков (~450 исполнителей), сплит train/val/test по артистам. Это важно: один исполнитель не может оказаться и в трейне, и в тесте, иначе модель просто запомнит, а не научится

Это было сделано всё ещё для тестов теории, так что вы можете взять и 30к losseles треков, отфильтровать их и обучить ещё более крутую модель

Как это честно померить, а не нарисовать красивую табличку

Чтож, пять метрик, две семьи.

Спектральные (расстояние до lossless-референса, меньше = лучше):

\mathrm{LSD} = \frac{1}{T}\sum_{t}\sqrt{\frac{1}{F}\sum_{f}\Big(10\log_{10}\frac{|X_r(f,t)|^2 + \epsilon}{|X_e(f,t)|^2 + \epsilon}\Big)^{\!2}}
  • LSD - по всей полосе; HF-LSD - только 14-22.05 кГц (там, где реставрация и происходит); MR-STFT - мультиразрешающее L1-расстояние лог-магнитуд (окна 512/1024/2048).

Сигнальные и перцептивные:

  • SI-SDR (больше = лучше) - точность волновой формы, безжалостна к фазовым ошибкам:

\mathrm{SI\text{-}SDR} = 10\log_{10}\frac{\|\alpha s\|^2}{\|\alpha s - \hat{s}\|^2}, \qquad \alpha = \frac{\langle \hat{s}, s \rangle}{\|s\|^2}
  • ViSQOL (audio-режим, 48 кГц) - перцептивная оценка MOS 1-5, гугловский алгоритм сравнения спектрограмм “как слышит человек”

Протокол: 100 held-out треков (артисты не пересекаются с обучающими) × 7 профилей деградации (mp3 96/128/192, aac 96/128, opus 64/96) = 700 сэмплов; обе системы получают байт-в-байт одинаковый деградированный вход; Apollo - реальный апстрим-чекпоинт в его штатной конфигурации 44.1 кГц

Серия 1, в которой я облажался

Первые недели всё шло подозрительно гладко: моя модель уверенно выносила Apollo по спектральным метрикам. Но SI-SDR вёл себя странно: вход после почти прозрачного MP3 128k показывал −13.7 dB. Вдумайтесь. Метрика на голубом глазу заявляла, что сигнал после кодека, который Вы на слух от оригинала не отличите, разрушен почти в труху.

Когда метрика говорит физически абсурдную дичь - виноват не сигнал, виноват ты. Полез адверсариально аудитить собственный пайплайн и нашёл причину. MP3-энкодер пихает в начало потока приминг-задержку: у LAME это 1105 сэмплов (25 мс), у AAC 1024 (23 мс). Деградированный сигнал был просто сдвинут относительно референса. Константный сдвиг - не искажение, его не слышно. А вот SI-SDR инвариантна к масштабу, но НЕ к сдвигу: при сдвиге почти вся энергия уходит в “шумовой” член, и метрика складывается на 25-33 dB.

И вот тут самое противное. Баг был направленно нечестен к Apollo. Пайплайн самих авторов Apollo эту задержку компенсирует (match2 + torch.roll в их датамодуле). То есть мой бенчмарк кормил чужую модель рассинхроненным входом, которого она в глаза не видела при обучении, и оценивал без выравнивания, которое заложили её авторы. Победа над SOTA на читерском бенче. Гордиться нечем.

Фикс - выравнивание по кросс-корреляции с фазовым взвешиванием (GCC-PHAT):

\tau^{*} = \arg\max_{\tau}\ \mathcal{F}^{-1}\!\left[\frac{X_d \cdot \overline{X_x}}{\big|X_d \cdot \overline{X_x}\big| + \epsilon}\right](\tau)

И полный перезапуск всех экспериментов на исправленных данных. Три моих собственных вывода после этого перевернулись с ног на голову:

  1. «Gate мешает» (был такой промежуточный вывод) - оказался артефактом бага. На выровненных данных gate обязателен: SI-SDR 21.7 против 9.7 без него.

  2. Гибридная схема (подмешивать бас Apollo ради SI-SDR) - потеряла смысл и уехала в архив: сохранённый кодеком бас точнее регенерированного.

  3. Сравнение с Apollo стало честным: его SI-SDR подскочил с ~−10 до +19 dB.

Мораль, которую я теперь повторяю как мантру: если Ваша модель бьёт SOTA - сначала ищите баг в бенчмарке. Особенно если метрика заявляет физическую дичь. Не верьте красивым цифрам, верьте перепроверенным.

Серия 2: парный бенч, теперь с математикой, а не на глаз

Дальше - больше. Внутренний «аудит готовности к публикации» (да, я сам себе аудитор, такая жизнь) нашёл вторую проблему, уже методологическую: итоговая таблица была склеена из двух разных прогонов - моя модель из одного запуска, Apollo из другого, - и нигде не считалась статистическая значимость. Хранились только средние. Так нельзя.

Поэтому финальный бенчмарк - строго парный (scripts/bench_paired.py): каждый из 700 сэмплов деградируется один раз, обе системы реставрируют один и тот же массив, все 700 пер-сэмпловых значений сохраняются, и по парным разностям считаются перцентильный bootstrap-CI (10 000 ресэмплов) и критерий знаковых рангов Уилкоксона. Вот теперь разговор предметный.

Результат (моя модель - с адаптивным gate, про него ниже):

Главный результат

Метрика

Вход (lossy)

north-star

Apollo

Δ (наша − Apollo)

95% CI

Wilcoxon p

Победы

LSD ↓

12.23

6.44

8.24

−1.80

[−1.95, −1.64]

7e−75

80%

HF-LSD ↓

19.45

9.43

11.42

−1.99

[−2.21, −1.76]

2e−53

75%

MR-STFT ↓

0.875

0.454

0.647

−0.193

[−0.208, −0.177]

1e−90

84%

SI-SDR ↑

21.98

21.84

19.09

+2.74

[+2.45, +3.04]

3e−87

89%

ViSQOL ↑

4.652

4.633

4.362

+0.271

[+0.242, +0.300]

1e−89

87%

5/5 метрик, каждый доверительный интервал исключает ноль. Модель в 1.9 раза меньше, обучена на одном маке, без чужих весов. Дайте я выдохну.

Но два уточнения, без которых эта таблица была бы маркетингом, а не наукой:

Почему выигран SI-SDR. Не потому, что я «восстанавливаю волну точнее». А потому, что gate не трогает низ, где кодек почти прозрачен (вход 21.98 → у меня 21.84), тогда как Apollo регенерирует весь спектр и теряет ~3 dB точности волновой формы. Это победа архитектурного решения «сохраняй, что сохранилось», а не магия. На полосе, которую реально надо восстанавливать, мой выигрыш виден по HF-LSD: 19.45 → 9.43 (на 10 dB ближе к оригиналу). Вот это уже честная работа.

Что с ViSQOL. Гляньте внимательно: вход - 4.652, и обе реставрации НИЖЕ. ViSQOL почти насыщен на около-прозрачных кодеках и штрафует любой синтез. Из этого наблюдения и родилось улучшение, ради которого стоило городить весь измерительный конвейер. Но об этом в Серии 3.

Мы выносим Apollo и на его “родном поле” - только MP3 (его обучающая деградация), n=300: все пять разностей значимы (например, SI-SDR +1.72 [+1.37, +2.07], ViSQOL +0.082 [+0.040, +0.125]). То есть победа - не артефакт того, что AAC/Opus для Apollo out-of-distribution. На его территории тоже бьём.

HF-LSD по кодекам

Серия 3: данные нашли дыру, и родился deficit-gated синтез

Разбивка ViSQOL по кодекам показала неприятное:

Профиль

ViSQOL вход

ViSQOL после реставрации (фикс. gate)

Δ

mp3-96k

4.538

4.462

−0.076

mp3-192k

4.705

4.521

−0.184

aac-128k

4.704

4.531

−0.173

opus-64k

4.636

4.636

0.000

Расшифровываю. На сильно порезанных входах (mp3-96k) синтез почти безвреден, на Opus - нейтрален, а вот на почти прозрачных профилях модель делала хуже. На aac-128k - даже по HF-LSD (6.08 на входе → 6.76 после «реставрации»): верх в файле уже есть, а модель его перерисовывала поверх. Сама себе вредитель.

Первая идея - детектить частоту среза и не лезть выше живой полосы - разбилась о реальность. Opus держит полную ширину полосы, но с дырами внутри (его потери временны́е и внутриполосные), а тёмные миксы неотличимы от порезанных по «потолку». Порог по спектру - хрупкая эвристика, которая развалится на первом же нестандартном треке.

Рабочее решение оказалось изящнее - deficit-gated synthesis: пусть модель сама решает, где она нужна. Для каждой время-частотной плитки (1 кГц × 5 кадров) считаем дефицит - насколько вход отстаёт от предсказания модели:

d(b, \tau) = \operatorname*{mean}_{f \in b}\big(L_{\mathrm{pred}} - L_{\mathrm{in}}\big), \qquad \lambda(b,\tau) = \mathrm{clip}\!\left(\frac{d - d_0}{d_1 - d_0},\, 0,\, 1\right)

с мёртвой зоной d_0 (отличия в пределах ошибки модели - не повод лезть) и насыщением d_1. Вклад модели взвешивается этой уверенностью:

\hat{L} = (1 - w)\,L_{\mathrm{in}} + w\,\Big[(1-\lambda)\,L_{\mathrm{in}} + \lambda \max\big(L_{\mathrm{in}}, L_{\mathrm{pred}}\big)\Big]

Логика простая. Если вход в полосе совпадает с ожиданием модели (d \approx 0) - полоса проходит нетронутой. Если в полосе кодековая дыра (d в десятки dB) - синтез врубается на полную. Никаких порогов на «частоту среза»: Opus с его внутренними дырами обрабатывается так же естественно, как кирпичная стена MP3.

Карта λ

Слева - вход (mp3-128k), в центре - предсказание модели, справа - карта \lambda: жёлтое - “модель включается” (вырезанный верх и временны́е дыры), тёмное - “вход уже в порядке, лапы прочь”.

Пороги d_0, d_1 я на глаз не тыкал (а очень хотелось). Трёхэтапная сетка (16 конфигов на стратифицированном подмножестве, затем два финалиста на полных 700 парах) показала чистое «колено»: ViSQOL-вред монотонно падает с ужесточением порогов, а HF-LSD-цена долго остаётся копеечной, потому что настоящие кодековые дыры имеют дефицит в десятки dB и проходят любой вменяемый порог. Финал: d_0 = 10, d_1 = 20 dB. Валидация на тех же 700 парных сэмплах против фиксированного gate:

Метрика

Δ (adaptive − fixed)

95% CI

Вердикт

ViSQOL ↑

+0.080

[+0.075, +0.085]

значимо лучше

SI-SDR ↑

+0.224

[+0.200, +0.250]

значимо лучше

MR-STFT ↓

−0.015

[−0.017, −0.013]

значимо лучше

LSD ↓

−0.016

[−0.038, +0.007]

не отличимо

HF-LSD ↓

+0.057

[+0.022, +0.092]

значимо, но пренебрежимо хуже

ViSQOL по кодекам: фиксированный против адаптивного

Перцептивный вред относительно входа сократился на 80% (с −0.099 до −0.020 [−0.025, −0.015]), на aac-128k реставрация перестала портить даже HF-LSD, а на самом порезанном профиле - mp3-96k - выход впервые обогнал по ViSQOL сам вход (4.547 против 4.538). И ничего не сломалось там, где синтез реально нужен: вся цена - +0.06 dB HF-LSD. Adaptive стал продакшен-дефолтом. Заслужил.

Бонусом: не верьте FAD-VGGish в задачах BWE

Для второй перцептивной оси я прикрутил FAD (Fréchet Audio Distance) - расстояние между распределениями эмбеддингов реставраций и lossless-оригиналов. Классический вариант на VGGish выдал странное:

Система

FAD-VGGish ↓

вход (lossy)

0.0228

наша (fixed gate)

0.0228

наша (adaptive)

0.0228

Apollo

0.1927

Мои цифры идентичны входу до четвёртого знака. Совпадение? Не думаю. Разгадка дурацкая: VGGish слушает на 16 кГц, то есть не видит ничего выше 8 кГц. Полосу, которую BWE и восстанавливает, метрика физически не анализирует. А ниже 8 кГц мой gate сохраняет вход бит-в-бит - вот и одинаковые числа. Если встретите в BWE-статье FAD-VGGish как доказательство качества - теперь Вы знаете, какой вопрос задать автору, чтобы он покраснел.

Но и слепой свидетель полезен. В полосе до 8 кГц (той самой, которую кодек сохранил почти без потерь) Apollo уехал от оригинала в 8 раз дальше, чем сам lossy-вход. Это независимое подтверждение главного тезиса: полную регенерацию спектра низ не прощает.

Тогда я посчитал FAD на музыкальном CLAP (48 кГц, видит весь спектр), и вот тут началось самое интересное в этой главе:

Система

FAD-CLAP ↓ (полная полоса)

вход (lossy)

0.085

наша (fixed)

0.083

наша (adaptive)

0.087

Apollo

0.037

Apollo здесь вдвое лучше всех. Включая сам lossy-вход. Противоречие с парным бенчем? А вот и нет. Это два разных вопроса. Парные метрики (LSD, SI-SDR, ViSQOL) спрашивают: насколько выход близок к оригиналу ЭТОГО трека? FAD спрашивает: насколько распределение выходов похоже на распределение музыки ВООБЩЕ? GAN Apollo регенерирует спектр в сторону «статистически типичной музыки», и семантические CLAP-эмбеддинги это обожают, даже когда конкретный трек уехал от своего оригинала (что парные метрики и ловят). Это классический perception-distortion trade-off, пойманный за руку и измеренный явно

Для задачи реставрации коллекции я сознательно стою на стороне distortion-полюса: Вы хотите услышать свой трек с возвращённым верхом, а не «правдоподобную музыку по мотивам». Но если бы я строил генератор красивых ремастеров “по мотивам” - рецепт Apollo был бы правильнее. Мораль: хорошая метрика - та, чей вопрос совпадает с Вашим. А не та, где цифра покрасивее.

А на чужой музыке как? (OOD)

Held-out сплит по артистам - это честно, но это всё ещё моя коллекция: похожие жанры, мастеринг, лейблы. Чтобы проверить переносимость, я собрал внешний сет из 51 lossless-трека с открытыми лицензиями - электроника, эмбиент и экспериментальные релизы с netlabels archive.org (CC-BY/CC-BY-SA, манифест с атрибуцией прилагается) плюс оркестровые public-domain записи Musopen - и прогнал тот же парный протокол (357 пар):

Метрика

Вход

north-star

Apollo

Δ (наша − Apollo)

95% CI

Победы

LSD ↓

10.37

6.66

9.90

−3.25

[−3.73, −2.80]

86%

HF-LSD ↓

15.98

9.44

12.32

−2.88

[−3.35, −2.43]

80%

MR-STFT ↓

0.846

0.511

0.861

−0.350

[−0.394, −0.310]

89%

SI-SDR ↑

20.68

20.46

17.23

+3.24

[+2.64, +3.85]

81%

ViSQOL ↑

4.640

4.620

3.992

+0.628

[+0.57, +0.69]

94%

Все пять побед остаются в силе, а на чужой музыке отрывы даже больше, чем на своей. Самое показательное - ViSQOL: полная регенерация Apollo на незнакомых жанрах (особенно оркестровых, которых кот наплакал в его MUSDB/MoisesDB-корпусе) проседает с 4.64 до 3.99, а консервативный gate-подход держит те же −0.020 до входа, что и на «своей» музыке. Деградации переноса нет вообще. Вывод однозначный: сохранять то, что кодек не тронул - стратегия, которая переносится на чужие жанры в разы лучше, чем регенерация всего подряд.

Третий боец: а если взять универсальный diffusion-SR?

Чтобы сравнение не выглядело дуэлью с единственным соперником, я прогнал тем же парным протоколом ещё и AudioSR (ICASSP 2024) - универсальный диффузионный super-resolution «любой вход → 48 кГц» с открытыми весами (модель basic, DDIM 50, фиксированный seed; выход выравнивал к референсу по GCC-PHAT, его вокодер тоже сдвигает волну). Подмножество: 15 треков × 7 кодеков = 105 пар:

Метрика

Вход

north-star

Apollo

AudioSR

Δ (наша − AudioSR)

Победы

LSD ↓

12.71

6.38

8.16

12.28

−5.90 [−6.36, −5.42]

98%

HF-LSD ↓

20.31

9.39

11.08

17.51

−8.12 [−8.84, −7.39]

98%

MR-STFT ↓

0.894

0.446

0.641

1.139

−0.69 [−0.75, −0.64]

100%

SI-SDR ↑

21.22

21.14

18.78

17.63

+3.51 [+2.29, +5.06]

100%

ViSQOL ↑

4.663

4.646

4.437

4.162

+0.48 [+0.44, +0.54]

100%

Результат поучительный: по спектральным метрикам AudioSR оказался хуже самого lossy-входа (LSD 12.28 против входных 12.71 - почти не починил, MR-STFT 1.14 против 0.89 - вообще ухудшил). И это не «AudioSR плохой». Это просто не его задача: он заточен под узкополосный вход (2-16 кГц) и полную пересборку сигнала через вокодер, а вход после музыкального кодека - широкополосный с дырами, и пересборка стирает больше, чем дорисовка возвращает. Урок ровно тот же, что и с Apollo, только громче: универсальный super-resolution ≠ реставрация кодека; чем больше система регенерирует, тем больше она теряет из того, что уже было в файле. Бонус практичности: диффузия - это ~50 секунд на 6-секундный клип на CPU; моя модель за это же время обрабатывает ~12 минут музыки. Чувствуете разницу.

Как этим пользоваться руками

Ежедневный сценарий - CLI, ничего лишнего:

# один файл
make restore IN=song.mp3                      # → restored/song.flac

# вся папка с lossy-библиотекой, рекурсивно, с пропуском уже сделанного
make restore IN=~/Music/lossy OUT=~/Music/hi ARGS="--recursive --skip-lossless"

На выходе - 44.1 кГц 24-бит FLAC, стерео (каналы восстанавливаются независимо), с тегами и обложкой исходника, дерево папок зеркалируется один-в-один. После первого запуска чекпоинт кэшируется в ~/.cache/north-star/, и дальше реставрация работает полностью офлайн, без поднятой инфраструктуры. Свежему клону достаточно задать NORTH_STAR_CKPT_URL - прямую ссылку на чекпоинт, и поехали.

Послушать можно прямо сейчас (фрагмент трека Artificial.Music - «Gold», CC-BY): вход mp3-128kвосстановлениеlossless-оригинал. Слушать лучше в наушниках, разница - в «воздухе» на тарелках и верхних гармониках синтов. На колонках ноута Вы её, скорее всего, не поймаете, не обессудьте.

Веб-интерфейс - для вдумчивого сравнения на слух:

Страница восстановления
Страница восстановления

Восстановление с A/B-сравнением и спектрограммами до/после.

Спектрограммы
Спектрограммы

А для исследовательского режима - живое обучение и сравнение экспериментов:

Обучение
Обучение
Эксперименты
Эксперименты

Что дальше

  • Phase-head для верхней полосы, добить последний источник артефактов.

  • Расширение OOD-бенчмарка и, может быть, общественный лидерборд реставрации.

Код, бенчмарки, скрипты и вся история исследования (включая баги, я их не прячу) - ТЫК. Если у Вас есть коллекция, которая дорога Вам так же, как мне моя, - буду рад issue, критике методологии и слепым прослушиваниям. В спорах рождается истина, а в адверсариальном аудите - нормальный бенчмарк.

Не болейте и берегите свой lossless