Определение bpm (beats per minute) в браузере

от автора

Мотивация

Когда-то я занимался спортивными парными танцами. Часто на тренировках была необходимость узнать темп (или скорость, если немного подушнить насчёт терминов) играющего трека, который измеряется в «ударах в минуту» (beats per minutebpm)

Спортсмены используют для этого разные сайты/приложения, где нужно пальцем «протапать» ритм. Я и сам таким пользовался, но однажды я задался вопросом — смогу ли я сделать браузерный сервис, который сможет определять bpm из записанного через микрофон аудио

Эта статья как раз о том, как я его сделал

Я не буду вдаваться в тонкости реализации непосредственно UI: у меня уже был на момент начала разработки пет-проект на React, и сервис я решил делать на базе него.

Принцип работы

Дадим возможность пользователю один раз тапнуть на кнопку Анализировать и будет показывать ему вычисляемый bpm, назовём его B, который со временем (с каждой итерацией анализа) будем уточнять:

  • Запускаем i-ый анализ

  • Записываем звук с микрофона длиной t_i

  • Вычисляем темп b_i

  • Уточняем значение B на основе b_i

После всех итераций значение B и будет тем bpm, что мы ищем.

Пока каждый пункт звучит мутно (кроме первого, пожалуй). Распишем их подробнее

Итерации анализа bpm

Определимся с количеством итераций, пусть это будет n. Так как мы будем записывать звук «кусками», то определим массив t_i их длин. Я экспериментировал с разными значениям t_i:

  • t_i = F_i, где F_i — i-ое число Фибоначчи

  • t_i = 2 * i

  • t_i = 2^i

В итоге остановился на варианте ∀i, t_i = t_0, где t_0 — константа.

В итоговой реализации взял значения n = 10 и t_0=6

Запись звука

Будем аписывать звук с микрофона через AudioWorklet, собирать сэмплы в Float32Array, а потом складывать их в AudioBuffer, который можно использовать для анализа.

Если расписывать этот этап подробнее по шагам, то получится следующее:

  • Получение доступа к микрофону

  • Создание AudioContext

  • Создание AudioWorklet для захвата аудиоданных

  • Подключение AudioWorklet к AudioContext

  • Подключение микрофона к ворклету

  • Сбор аудиоданных

  • Финальная сборка в AudioBuffer

  • Возврат результата

Код можно посмотреть здесь

Вычисление темпа bi

Сделаем метод getBPMFromAudioBuffer, который вычисляет примерный темп b_i трека из аудиобуфера, анализируя пики энергии сигнала (предполагаемые удары) и измеряя интервалы между ними.

Распишем пошагово, что будет делать метод

Преобразование звука в моно

На каждой позиции берётся среднее значение левого и правого канала. Это упрощает дальнейшую обработку.

Разделение сигнала на окна и расчёт энергии

Сигнал разбивается на кусочки (или «окна», в моём случае такой hopSize = 512 сэмплов). Для каждого куска считается энергия («громкость» в каждом фрагменте времени): сумма квадратов амплитуд в окне.

Поиск пиков энергии (ударов)

Вычисляется средняя энергия по всему сигналу. Удар (onset) считается найденным, если

  • энергия окна выше средней

  • энергия локально максимальна (больше соседей слева и справа)

  • время удара рассчитывается из номера окна и длины сэмплов

Расчёт bpm по интервалам между ударами

Находятся интервалы между ударами (в секундах). Оставляются только интервалы, которые соответствуют 30–240 BPM, чтобы убрать шум. Из каждого интервала считается bpm = 60 / interval. Собирается гистограмма (сколько раз встречался каждый bpm). Выбирается самый частый bpm — он считается результатом bi для i-ого анализа.

Код можно посмотреть здесь

Уточнение итогово значения темпа B

После каждого анализа мы хотим обновлять итоговое значение, применяя некий метод для уже посчитанных b_i.

Имперически получилось выяснить, что среднее и медиана не дают близкий к реальности результат, в отличие от экспоненциальгого скользящего среднего (ema) с коэффициентом α = 0.2, точность которого составила 95% на тестовых данных.

B = ema(b_i)

Код можно посмотреть здесь

Итог

После выполнения всех итераций итоговым темпом играющего трека будет значение B.

Посмотреть реализацию можно на Radio Hustle

Оригинальная статья на GitHub


ссылка на оригинал статьи https://habr.com/ru/articles/904542/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *