AudioTrove — инструмент для очистки и подготовки аудиоданных для TTS. Узнайте, как автоматизировать препроцессинг и сэкономить часы. Попробуйте прямо сейчас!
Каждый раз, когда я начинаю проект по файнтюнингу TTS, я теряю первый день на одни и те же задачи. Не на обучение модели и не на подбор гиперпараметров, а на препроцессинг аудио: выяснить, какие клипы достаточно чистые для обучения, обрезать тишину по краям, конвертировать всё в нужный формат и написать манифест в формате, который требует тренер на этой неделе. Это не сложная работа, но она одинаковая каждый раз, и мой скрипт, который я собираю на коленке, ломается каждый раз по-новому. В прошлый раз он умер через 3 часа ночного прогона. Без возможности возобновления, без лога обработанных файлов — просто полупустая папка и осознание, что надо начинать с нуля. Поэтому я создал AudioTrove.
AudioTrove — это CLI-инструмент и Python-библиотека, которая превращает папку с сырым аудио в чистый, готовый к обучению датасет. Команда выглядит так:
pip install audiotrove
audiotrove curate ./recordings ./output --ttsУкажите путь к аудио, путь к выходной папке, добавьте флаг --tts для запуска пайплайна подготовки TTS. На выходе вы получите:
filelist.txt в формате F5-TTS (табуляция: путь, длительность, текст)metadata.csv в формате LJSpeech (разделитель: имя файла, текст, текст)checkpoint.db — SQLite-база данных, логирующая каждый обработанный файл по хешу содержимогоSQLite-чекпоинт — это то, что мне важнее всего. Если запуск прервётся по любой причине, вы просто повторяете ту же команду. Файлы, которые уже были обработаны, находятся по хешу и пропускаются сразу. Запуск продолжается с того места, где остановился.
«Курирование» — расплывчатое слово, поэтому вот что происходит на самом деле.
Не всё аудио содержит речь. Многое содержит музыку, фоновый шум или тишину, которая выглядит как аудио на уровне файла. Silero VAD оценивает каждый клип по доле кадров с реальной речью. Клипы ниже порога отбраковываются здесь, до запуска остальных этапов. Это самый важный фильтр во всём пайплайне. Если в клипе нет речи, ничто ниже по конвейеру это не исправит.
Начальная и конечная тишина обрезается в каждом клипе. Отступы настраиваются в кадрах, а не в секундах, что важно при работе с файлами с разной частотой дискретизации и чтобы оценки длительности не «плыли».
SNR (отношение сигнал/шум) — это мера того, насколько речь громче фонового шума. Низкий SNR означает, что клип слишком шумный для надёжного обучения, и его, вероятно, не стоит включать в датасет. AudioTrove оценивает SNR с учётом VAD: уровень шума вычисляется из неречевых кадров, а уровень сигнала — из речевых. Это даёт гораздо более чистое разделение, чем наивное глобальное RMS-отношение, особенно для клипов с естественными паузами, где глобальный расчёт занижал бы уровень шума. Никакой зависимости от scipy, всё на чистом PyTorch.
Длительность проверяется после обрезки, а не до неё. То есть вы фильтруете по реальной длительности контента, а не по сырой длине файла. Файл длительностью 10 секунд с 8 секундами тишины в начале будет измерен как примерно 2 секунды контента, а не 10.
Выжившие клипы экспортируются как WAV 16 кГц моно. Частота дискретизации фиксирована, потому что это то, что ожидает Silero VAD внутри, и это также самая распространённая целевая частота для TTS-тренеров.
Оба манифеста (LJSpeech и F5-TTS) записываются атомарно в конце каждого батча. Каждый обработанный файл записывается в checkpoint.db с его хешем, чтобы повторные запуски могли пропускать его без необходимости заново читать файл.
Тестировалось на LibriSpeech dev-clean: 2703 клипа, около 5.4 часов аудио.
Никакого GPU ни на одном этапе. Узкое место — I/O и инференс VAD, оба CPU-bound. Стоит отметить: масштабирование на несколько воркеров скромнее, чем можно ожидать. 4 воркера дают примерно 1.57x ускорение вместо 4x, потому что запись манифестов и вставка в чекпоинт сериализуются в главном процессе во избежание повреждений. Это известное ограничение; режим WAL в SQLite с пакетной записью — следующее, что я хочу попробовать.
Скачайте подкаст с помощью yt-dlp, разделите на главы, запустите AudioTrove на выходе. Вы получите чистые клипы по предложениям с filelist, уже отформатированным для F5-TTS. Весь процесс занимает около 20 минут на CPU ноутбука для типичного часового эпизода.
Аудиокниги обычно уже чистые и хорошо темпоритмированные, поэтому фильтры VAD и SNR в основном пропускают всё. Главная ценность AudioTrove здесь — генерация манифестов и конвертация формата, что иначе заняло бы пару часов скриптования.
Аудио интервью часто грязное: перекрёстные разговоры, разное качество микрофонов, фоновый шум. VAD-фильтрация и SNR-оценка автоматически удаляют худшие клипы. Добавьте --tts-diarize для сегментации по спикерам, если имеете дело с разговором двух людей.
Похоже на интервью, но обычно один спикер и более стабильное качество аудио. Обрезка тишины особенно полезна здесь, потому что в лекциях бывают длинные паузы, которые иначе завышали бы оценку длительности.
# Добавить диаризацию спикеров (требуется Hugging Face токен для pyannote)
audiotrove curate ./audio ./output --tts --tts-diarize --tts-hf-token YOUR_TOKEN
# Добавить транскрипцию Whisper для каждого клипа
audiotrove curate ./audio ./output --tts --tts-transcribe
# Запустить с 4 параллельными воркерами
audiotrove curate ./audio ./output --tts --workers 4
# Обработать FLAC-файлы (по умолчанию только WAV)
audiotrove curate ./audio ./output --tts --extensions flac
# Сегментировать длинные файлы по границам VAD перед курированием
audiotrove curate ./audio ./output --tts --segmentAudioTrove также предоставляет внутренние компоненты для создания собственных пайплайнов:
from audiotrove import AudioDocument, AudioFilter, AudioTransformer
# Кастомный фильтр: отклонять клипы с пиковой амплитудой ниже порога
class PeakFilter(AudioFilter):
name = "min_peak"
def __init__(self, min_peak=0.01):
self.min_peak = min_peak
def filter(self, doc: AudioDocument) -> bool:
return float(abs(doc.audio).max()) >= self.min_peak
# Кастомный трансформер: нормализация до целевой пиковой амплитуды
class NormalizeTransformer(AudioTransformer):
name = "normalize"
def transform(self, doc: AudioDocument) -> AudioDocument:
peak = abs(doc.audio).max()
if peak > 0:
doc.audio = doc.audio / peak * 0.95
return docЛучше скажу сразу, чем вы обнаружите это после прогона на 10 часах аудио.
checkpoint.db, и вы можете запросить её вручную, но она должна быть в сводном выводе.Все это — открытые вопросы. Если какие-то из них вам интересны, в CONTRIBUTING.md есть инструкция по настройке.
pip install audiotrove
audiotrove curate ./your-audio ./output --tts --extensions flacGitHub: https://github.com/onepizzateam/AudioTrove
Если вы создали что-то похожее или у вас есть мнение о подходе к оценке SNR или дизайне чекпоинтов (или о чём угодно), я буду рад услышать.
Хочешь закрепить знания на практике?
Решай задачи на Algolit — интерактивная платформа для обучения
Начать бесплатно →