Skip to content

WoIfram/audio2audio

Repository files navigation

disclaimer

This project is abandonned for indefinite time, although I'm planning to return and make a fast and user-friendly version of the program. At the moment you can only experiment by editing config.py file and running main.py. If you have any questions related to the usage of the script or the algorithm, feel free to e-mail me at wolframep@yandex.ru in English or Russian


Этот проект заброшен на неопределённое время, хотя я планирую вернуться к нему и написать его быструю и юзабельную версию. На данный момент вы можете поэкспериментировать, изменяя настройки config.py и запуская main.py. Если у вас есть какие либо вопросы по алгоритму или использованию скрипта, можете писать на мыло wolframep@yandex.ru на русском или английском языке

audio2audio

Скрипт предназначен для автоматического сдвига тайминга субтитров. Т.е. имеется два рипа одного и того же кинца, и субтитры к одному из них. На основе их звукодорожек скрипт должен сопоставить временные метки субтитров моментам второго видео и вернуть четвёртый файл - субтитры ко второму видео, а также вывести информацию о том, в какие местах были произведены сдвиги субтитров: на практике бывают случаи, когда в первом рипе не хватает куска, поэтому это нужно, чтобы можно было просмотреть вручную подозрительные места. Собственно, есть идея вывести ещё и пятый файл - сконструировать из кусков второго видео, отсутствующих в первом, новое видео, которое можно будет просмотреть и проверить, нет ли там новых реплик.

Формат ввода и вывода

Все данные для запуска скрипта, в частности, путь к медиафайлам и субтитрам, находятся в файле config.py. Надеюсь, по приведённому примеру и комментариям понятно, что каждая из переменных делает. Важные переменные:

  • MEDIA: Должно быть указано либо два видео в списке (с какого рипа на какой двигать), либо пустой список. Если список пустой, предполагается, что программа была запущена ранее, и необходимые данные для сдвига видео находятся в файле LOG_FILE, куда они и записываются.
  • ASS_FILES: Список произвольной длины из файлов субтитров, подвергающихся сдвигу.
  • Если вас напрягают временные файлы, остающиеся после работы программы, можете установить SAVE_FILE=False. Однако эти временные файлы используются без переподсчёта, если вы запускаете прогу на тех же видео ещё раз (например, для сдвига с большей точностью).

Выводит скрипт информацию о процессе: текущую точность вычисления пути в графе, затраченное время на каждый из путей и т.п. Вывод p(x, y) означает, что поиск в настоящий момент находится в вершине графа с такими координатами, позже переделаю, чтобы выводила процент завершённого вместо этого. Каждый файл субтитров *.ass, подвергающийся сдвигу, порождает файл вида *_shifted.ass в той же директории.

Установка

Нужен Python 3.5 и библиотеки numpy, scipy. Также я не нашёл более простого способа работы с видео и аудио, кроме как запускать команды FFmpeg из питона, так что FFmpeg тоже должен быть установлен и прописан в PATH. Если вы предпочитаете другой способ конвертации видео в аудио данной частоты, пропишите его вместо call('ffmpeg ... в main.py : эта строчка должна вытаскивать из видео %filename% аудиофайл, сохраняя его по расположению %wav_file% с частотой %Config.DEFAULT_HZ%. После прописывания всех данных в конфиге запустите main.py. (Текущий конфиг позволяет построить сдвиг между двумя рипами s06e24. Чтобы не загромождать место, они были конвертированы в mp3, но скрипт с таким же успехом работает с mkv.)

Алгоритм

Разобьём аудиодорожки на много мелких кусочков одинаковой длины, эту длину обзовём тиком. Теперь возьмём все куски (накладывающиеся, разумеется) по B_OVERLAP_DEGREE подряд стоящих тиков (B_OVERLAP_DEGREE=3 в текущей реализации) и подсчитаем спектр для каждого из них. Получим две спектрограммы, которые нужно сопоставить друг другу по похожести. Между любыми двумя спектрами есть некоторое расстояние, подробности см. в коде [функция cos_log из spectrum.py]. Мы хотим, чтобы сопоставлялись куски с как можно меньшим суммарным расстоянием.

Построим граф-решётку, вершинами которого являются пары (спектр номер A из первой спектрограммы, спектр номер B из второй спектрограммы), а рёбра из каждой вершины выходят в трёх направлениях - в (A, B+1) [вертикальное], (A+1, B) [горизонтальное], (A+1, B+1) [диагональное]. Вес диагонального ребра равен расстоянию между спектрами A и B, а горизональных/вертикальных - некоторой константе (NONDIAGKOEF=1.3 в коде) * среднее расстояние между двумя спектрами (предпосчитывается тысячей рандомных подсчётов расстояний и взятием среднего). Теперь мы ищем кратчайший путь из (0, 0) в противоположный конец прямоугольника. В этом пути диагональным участкам будут соответствовать соответствия в видео, а горизонтальным и вертикальным - вырезанные участки. Так как обычно кусков, на которые разбито видео, не очень много, можно ввести довольно приличный штраф за переход с гор./верт. рёбер на диагональные и наоборот. В текущей версии он равен PENALTY=15 * среднее расстояние между двумя спектрами. Предполагаю, что нужно ввести зависимость этой величины не только от среднего расстояния между спектрами, но и от длины спектрограмм, которые будут довольно короткими для больших размеров тика (см. далее).

Проблема в том, что на реальных файлах поиск кратчайшего пути в таком графе работает долго, даже с использованием некоторых оценок снизу на расстояние. Поэтому мы применяем следующую эвристику, довольно стабильно наблюдающуюся: если мы рассмотрим кратчайшие пути для спектрограмм с тиком T и c тиком T/2, то эти пути не будут сильно друг отходить. Поэтому можно взять изначально огромный размер тика, чтобы одна из сторон решётки не превосходила 2 по длине, и при этом был бы равен 2^(нечто) * финальный размер, к которому мы стремимся. После этого можно делить размер тика пополам, ища каждый новый путь в эпсилон-окрестности старого, что делается за линию обычной динамикой. (Для каждой вершины хранятся два лучших пути: заканчивающихся на гор./верт. ребро, и на диагональное ребро, чтобы можно было учитывать штрафы за переходы.)

Заметим, что спектрограмма строится по исходному сигналу только один раз - для размера тика, равному BASE_TICK=0.01 секунды. Спектрограммы для больших размеров тиков получаются усреднениями векторов в этой базовой спектрограмме. А именно, если нам нужно получить спектрограмму для тика длины BASE_TICK * W, то мы разбиваем спектрограмму на кусочки по W бейз-тиков, после чего усредняем по каждым C_OVERLAP_DEGREE подряд стоящим таким кусочкам.

About

Tools for searching best match of two audio files and retiming of subtitles

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages