- BrainTools - https://www.braintools.ru -

Чистим аудиокниги от шума нейросетями

TL;DR: Классические фильтры (FFmpeg, Audacity) плохо справляются со сложным шумом в аудиокнигах. Нейросети для source separation работают в разы лучше. Написал обертку над audio-separator, которая умеет обрабатывать многочасовые файлы без вылетов по памяти [1].

В прошлой статье [2] я рассказывал про go-audio-converter — конвертер аудио на чистом Go без FFmpeg. Сегодня — про следующий инструмент в моей аудио-экосистеме: очистку аудиокниг от шума с помощью нейросетей.

Проблема

Я фанат аудиокниг и подкастов. Одна из любимых — «Модель для сборки», культовый проект Влада Копа с фантастическими рассказами под эмбиент. Проблема: ранние выпуски 2000-х записаны так, что музыка местами забивает голос диктора. Слушать сложно — приходится напрягаться, чтобы разобрать текст.

Особенно проблемно на 3 из моих самых любимых книг – Цикл про богатыря Жихаря, Михаила Успенского (рекомендую)

И это не единичный случай. В моей коллекции полно аудиокниг с похожими проблемами:

  • Фоновая музыка громче голоса

  • Гул 50/60 Гц от старого оборудования

  • Шипение от кассетных оцифровок

  • Эхо и гулкость помещения

Первая мысль — FFmpeg:

bash

ffmpeg -i input.mp3 -af "highpass=f=200, lowpass=f=3000, anlmdn" output.mp3

Не работает. Фоновая музыка сидит в тех же частотах, что и голос. Режешь частоты — режешь голос. Noise reduction (anlmdn) справляется только с простым шумом типа шипения.

Audacity с плагинами чуть лучше, но результат всё равно так себе — либо музыка остаётся, либо голос звучит как из-под воды.

Проблема №2: длинные файлы

Допустим, нашел инструмент который работает. UVR5 (Ultimate Vocal Remover) с нейросетями реально отделяет голос от музыки. Но попробуй скормить ему 7-часовую аудиокнигу.

  • UVR5 — GUI зависает, вылетает с “out of memory” на файлах >1 ГБ

  • Demucs CLI — то же самое, жрёт всю память и падает

  • Онлайн-сервисы — лимит 10-20 минут на файл

Аудиокнига — 7 часов. В WAV это около 3 ГБ. Ни один из существующих инструментов не умеет это нормально обрабатывать. Либо режь вручную на куски, обрабатывай по одному, потом склеивай — но это часы ручной работы на каждую книгу.

Нужен был инструмент, который:

  1. Работает с файлами любой длины

  2. Не падает по памяти

  3. Автоматизируется (batch processing)

  4. Сохраняет прогресс (упал — продолжил)

Почему нейросети?

Source separation — задача отделения источников звука друг от друга. Музыкальная индустрия давно использует это для караоке: отделить вокал от инструментов.

Ключевое наблюдение: голос диктора — это тот же “вокал”. Модели, обученные отделять вокал от музыки, отлично справляются с аудиокнигами.

Основные архитектуры:

  • MDX-Net — быстрая, хорошее качество, работает на слабом железе

  • Demucs (Meta) — универсальная, но тяжёлая

  • Roformer — трансформеры для аудио, лучшее качество, но медленная

Выбор инструмента

Есть несколько способов использовать эти модели:

  1. UVR5 (Ultimate Vocal Remover) — GUI-приложение, удобное, но не автоматизируешь

  2. Demucs CLI — только модели Demucs

  3. audio-separator — Python-библиотека, поддерживает все модели UVR5

Выбрал audio-separator [3] — это те же модели, что в UVR5, но с нормальным API для автоматизации.

Архитектура решения

┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│ Входной     │ → │ Нарезка на   │ → │ MDX-Net/    │
│ файл (7ч)   │    │ чанки (5мин) │    │ Roformer    │
└─────────────┘    └──────────────┘    └─────────────┘
                                              ↓
┌─────────────┐    ┌──────────────┐    ┌─────────────┐
│ Результат   │ ← │ Склейка      │ ← │ Vocals +    │
│ .mp3        │    │ ffmpeg       │    │ DeNoise     │
└─────────────┘    └──────────────┘    └─────────────┘

Главная проблема — память. 7-часовая аудиокнига в WAV — это ~4 ГБ. Нейросеть хочет загрузить всё в память, плюс промежуточные тензоры. На 16 ГБ RAM получаем “bad allocation”.

Решение: чанкование. Режем файл на части по 5 минут, обрабатываем каждую отдельно, склеиваем обратно.

Реализация

Нарезка на чанки

def split_audio(input_file: str, output_dir: Path, chunk_duration: int) -> list[Path]:
    """Нарезает аудиофайл на части указанной длительности."""
    pattern = output_dir / "chunk_%04d.wav"
    
    subprocess.run([
        'ffmpeg', '-y', '-i', str(input_file),
        '-f', 'segment', '-segment_time', str(chunk_duration),
        '-c:a', 'pcm_s16le',
        str(pattern)
    ], capture_output=True, check=True)
    
    return sorted(output_dir.glob("chunk_*.wav"))

FFmpeg умеет резать точно по времени без перекодирования. Конвертируем в WAV (PCM), потому что нейросети ожидают несжатый вход.

Обработка чанка

from audio_separator.separator import Separator

separator = Separator(
    output_dir=str(chunk_output_dir),
    output_format='wav',
    sample_rate=44100,
    use_autocast=use_gpu,  # Mixed precision для GPU
    mdx_params={
        "hop_length": 1024,
        "segment_size": 64,
        "overlap": 0.25,
        "batch_size": 1,
    }
)

# Шаг 1: Отделяем голос от музыки
separator.load_model('UVR-MDX-NET-Voc_FT.onnx')
result = separator.separate(str(chunk_path))
# Получаем: chunk_0001_(Vocals).wav, chunk_0001_(Instrumental).wav

# Шаг 2: Убираем остаточный шум из вокала
separator.load_model('UVR-DeNoise.pth')
denoised = separator.separate(str(vocals_file))
# Получаем: chunk_0001_(Vocals)_(No Noise).wav

Двухэтапная обработка:

  1. MDX-Net отделяет голос от всего остального (музыка, шумы)

  2. DeNoise убирает остаточное шипение и гул

Сохранение прогресса

Критично для многочасовых файлов — процесс может упасть на середине:

# Проверяем, обработан ли уже чанк
existing_vocals = list(chunk_output_dir.glob("*Vocal*.wav"))
if existing_vocals:
    logger.info(f"Пропуск (уже обработан): {chunk.name}")
    continue

Промежуточные файлы сохраняются в chunks/ и processed/. Если скрипт упал — перезапускаем, он продолжит с того места.

Параллельная обработка

Для CPU-режима добавил параллелизм через multiprocessing:

def _process_chunk_worker(args: tuple) -> dict:
    """Воркер для параллельной обработки чанка."""
    chunk_idx, chunk_path, chunk_output_dir, model_name, ... = args
    
    # Каждый воркер создаёт свой экземпляр Separator
    separator = Separator(...)
    separator.load_model(model_name)
    result = separator.separate(str(chunk_path))
    
    return {'idx': chunk_idx, 'vocals': vocals, 'instrumental': instrumental}

# Запуск пула воркеров
with mp.Pool(processes=workers) as pool:
    for result in pool.imap(_process_chunk_worker, tasks):
        # Обрабатываем результаты по мере готовности
        ...

На 6-ядерном CPU это даёт ускорение в 3-4 раза.

Склейка результата

def concat_audio(input_files: list[Path], output_file: Path, output_format: str):
    """Склеивает аудиофайлы в один."""
    list_file = output_file.parent / "concat_list.txt"
    
    with open(list_file, 'w') as f:
        for file in input_files:
            abs_path = str(file.absolute()).replace('\', '/')
            f.write(f"file '{abs_path}'n")
    
    codec_args = {
        'mp3': ['-c:a', 'libmp3lame', '-q:a', '2'],
        'flac': ['-c:a', 'flac'],
        'wav': ['-c:a', 'pcm_s16le'],
    }[output_format]
    
    subprocess.run([
        'ffmpeg', '-y', '-f', 'concat', '-safe', '0',
        '-i', str(list_file),
        *codec_args,
        str(output_file)
    ])

FFmpeg concat без перекодирования WAV-чанков, финальное сжатие в MP3/FLAC на последнем шаге.

Выбор модели

Протестировал на своей коллекции:

Модель

Качество голоса

Скорость (GPU)

VRAM

Когда использовать

UVR-MDX-NET-Voc_FT

★★★★☆

~30 сек/5мин

2 ГБ

По умолчанию

Kim_Vocal_2

★★★★☆

~30 сек/5мин

2 ГБ

Альтернатива, иногда лучше

BS-Roformer

★★★★★

~2 мин/5мин

4 ГБ

Максимальное качество

UVR-DeNoise

~15 сек/5мин

1 ГБ

Второй проход для шума

Рекомендация: MDX-Net для большинства случаев. Roformer — если качество критично и есть время.

Использование

# Базовая очистка
python audiobook_cleaner.py audiobook.mp3

# Большой файл — обязательно с чанкованием
python audiobook_cleaner.py audiobook.mp3 --chunk-duration 300

# Без второго прохода DeNoise (быстрее)
python audiobook_cleaner.py audiobook.mp3 --chunk-duration 300 --no-denoise

# Максимальное качество (Roformer)
python audiobook_cleaner.py audiobook.mp3 --model roformer --chunk-duration 300

# Обработка директории
python audiobook_cleaner.py --dir ./audiobooks --output ./clean

# На слабом железе
python audiobook_cleaner.py audiobook.mp3 --chunk-duration 120 --segment-size 16 --cpu

Производительность

Время обработки 5-минутного чанка:

Конфигурация

Время

Примечание

CPU (i7-8700)

2:30

Без GPU

GPU GTX 1660 (PyTorch fallback)

1:40

ONNX → PyTorch

GPU GTX 1660 (ONNX native)

0:30-0:40

Полное ускорение

7-часовая аудиокнига (85 чанков по 5 минут):

  • CPU: ~3.5 часа

  • GPU (ONNX): ~45-60 минут

Подводные камни

1. ONNX Runtime и CUDA

audio-separator использует ONNX Runtime для MDX-Net моделей. Чтобы GPU работал:

pip install onnxruntime-gpu==1.18.1
pip install nvidia-cudnn-cu12==8.9.7.29  # Критично!

Без cuDNN получите “CUDAExecutionProvider not available” и fallback на CPU.

2. Segment size vs dim_t

Если в логах видите:

Model converted from onnx to pytorch due to segment size not matching dim_t

Модель конвертируется в PyTorch на лету — это медленнее в 3-4 раза. Не критично, но знайте.

3. Артефакты на стыках чанков

Теоретически могут быть щелчки на границах чанков. На практике FFmpeg concat справляется — не заметил проблем на 200+ часах обработки.

4. DeNoise убивает тихие места

Модель DeNoise иногда слишком агрессивна на тихих фрагментах (паузы между фразами). Если это критично — используйте --no-denoise.

Результаты

Обработал ~200 часов аудиокниг. Субъективно:

  • Фоновая музыка — убирается на 90-95%, остаются лёгкие “призраки”

  • Гул и шипение — убирается почти полностью

  • Голос — сохраняется без заметных искажений

PS.

Инструмент подходит не только для аудиокниг:

  • Минусовки — те же модели работают в обратную сторону: берёте трек, получаете инструментал без вокала.

  • Архивные записи — я увлекаюсь авиацией и расследованиями катастроф, использовал для очистки записей бортовых самописцев. Выделить голоса экипажа из шума двигателей — та же задача source separation.

Ссылки

Автор: formeo

Источник [6]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/24922

URLs in this post:

[1] памяти: http://www.braintools.ru/article/4140

[2] прошлой статье: https://habr.com/ru/articles/985442/

[3] audio-separator: https://github.com/nomadkaraoke/python-audio-separator

[4] GitHub: Audiobook-Cleaner: https://github.com/formeo/Audiobook-Cleaner

[5] Ultimate Vocal Remover: https://github.com/Anjok07/ultimatevocalremovergui

[6] Источник: https://habr.com/ru/articles/986738/?utm_campaign=986738&utm_source=habrahabr&utm_medium=rss

www.BrainTools.ru

Rambler's Top100