Я делаю музыкальный плеер Nami. Он показывает у каждого трека тональность: «ми минор», «ре мажор». После поста про плеер в комментариях спросили, как это вообще считается. Вопрос звучал примерно так: у Прокофьева в мажорной тональности мажора на слух почти нет, так как программа решает?
Короткий ответ: программа не слушает гармонию, как музыкант. Она считает, сколько в записи звучит каждой из 12 нот, и ищет мажорную или минорную гамму, на которую этот набор похож сильнее всего. Ниже я разберу, как это устроено, покажу картинки на реальных треках и расскажу, где метод ошибается.
Шаг 1. Хромаграмма: сколько звучит каждой ноты
Плеер берёт первые 90 секунд трека, сводит их в моно и понижает частоту до 22 050 Гц. Дальше запись режется на окна по 4096 отсчётов (около 0,19 с) с перекрытием в половину окна. Каждое окно проходит через быстрое преобразование Фурье, и получается спектр: сколько энергии на каждой частоте.
Частоту легко перевести в ноту. Ля первой октавы звучит на 440 Гц, каждый полутон умножает частоту на ¹²√2. Номер ноты по стандарту MIDI:
midi = 69 + 12 · log2(f / 440)
Октава при этом не важна: до в любой октаве считается одной нотой. Остаток от деления на 12 даёт класс ноты от 0 (до) до 11 (си). Амплитуды всех частот одного класса складываются, и получается 12 чисел. Это хромаграмма. Частоты ниже 80 Гц и выше 5000 Гц я отбрасываю: внизу гудит бас и бочка, вверху шипят тарелки и обертоны.
for k in 1..FFT_SIZE / 2 {
let freq = k as f64 * rate as f64 / FFT_SIZE as f64;
if !(80.0..=5000.0).contains(&freq) {
continue;
}
let mag = (re[k] * re[k] + im[k] * im[k]).sqrt();
let midi = 69.0 + 12.0 * (freq / 440.0).log2();
let pc = (((midi.round() as i64 - 60) % 12 + 12) % 12) as usize;
chroma[pc] += mag;
}
Шаг 2. Профили: как «должна» звучать тональность
Хромаграмму нужно с чем-то сравнить. Для этого есть профили Крумхансл и Кесслер. В 1982 году психологи Кэрол Крумхансл и Эдвард Кесслер ставили слушателям с музыкальным образованием каденцию (короткую последовательность аккордов) в нужной тональности, потом играли одну ноту и просили оценить, насколько она «подходит». Так получились 12 оценок для мажора и 12 для минора.
Выше всего оценена тоника, потом квинта и терция. Ноты не из гаммы оценены ниже всего. В мажоре и миноре разница в основном в терции: ми в до мажоре и ми-бемоль в до миноре.
Профиль построен для до. Для другой тоники его сдвигают по кругу: профиль ре мажора – это профиль до мажора, сдвинутый на два полутона. 12 тоник на 2 лада дают 24 профиля.
Шаг 3. Корреляция: какая гамма похожа сильнее
Осталось сравнить хромаграмму с каждым из 24 профилей. Я беру обычный коэффициент корреляции Пирсона: он не зависит от громкости записи и показывает только похожесть формы. Тональность с наибольшей корреляцией и есть ответ. Этот способ называют алгоритмом Крумхансл-Шмуклера, Крумхансл описала его в книге 1990 года.
Вот как это выглядит на треке, где тональность выражена ясно:
Громче всего звучит ми, тоника. Среди первых и соль, минорная терция. Ля, четвёртая ступень, звучит громче, чем ждёт профиль, поэтому корреляция 0,845, а не 1, и второе место у ля минора.
Так выглядят все 24 оценки для этого трека и для другого, где ответ неочевиден:

В первом треке ми минор выигрывает с запасом: 0,845 против 0,703 у второго места. Во втором до минор и соль минор отличаются на сотую. Плеер всё равно напишет «до минор», хотя честнее было бы сказать «где-то между до и соль минором».
Весь алгоритм — около 110 строк на Rust без внешних библиотек, вместе с самописным БПФ. На сервере он считает тональность и темп при сканировании библиотеки. Тот же алгоритм на Kotlin работает на телефоне для локальных файлов.
Где метод ошибается
Параллельные и родственные тональности. У до мажора и ля минора одни и те же семь нот. Различаются они только тем, какие ноты звучат чаще. Если в песне много аккорда ля минор, а заканчивается она в до мажоре, хромаграмма этого не различит: она не знает, какой аккорд был последним.
Соседи по квинтовому кругу. У до мажора и соль мажора шесть общих нот из семи. Отсюда ничья на картинке выше: до минор и соль минор тоже очень близкие соседи.
Модуляции. Хромаграмма по 90 секундам усредняет всё, что в них прозвучало. Если трек сменил тональность, получится смесь. Вот хромаграмма одной песни по 10-секундным кускам:
Почти каждый кусок даёт свой ответ. Общая корреляция по первым 90 секундам у этого трека всего 0,31 для ля минора и 0,29 для ре мажора. Для такой песни одна тональность — условность, и программа это честно показывает низкими числами. Жаль, что пока только в числах: на экране видна одна строчка.
Лады и хроматика. Профили описывают только классические мажор и минор. Дорийский лад, блюз, музыка, где автор нарочно уходит от тоники, или Прокофьев с его хроматикой подгоняются под ближайшую из 24 тональностей. Это ровно тот случай из комментария. Тональность в названии симфонии — решение композитора о центре всей вещи. Алгоритм же видит только, каких нот в записи больше.
Что звучит, кроме нот. Барабаны, перегруженные гитары и шум добавляют энергию на всех частотах. Обертоны тоже путают: у ноты до третья гармоника звучит как соль. Поэтому я режу диапазон до 80–5000 Гц, но это полумера.
Строй. Если запись настроена не на 440 Гц, а, например, на 432, часть энергии попадает в соседний полутон. Метод это не компенсирует.
А нейросети?
С 2005 года алгоритмы определения тональности ежегодно сравнивают на конкурсе MIREX. Там оценка не просто «угадал или нет»: точное попадание даёт 1 балл, ошибка на квинту 0,5, родственная тональность 0,3, параллельная 0,2. Сами правила говорят, что ошибки в соседние тональности ожидаемы.
Современные системы учат свёрточную нейросеть на спектрограммах, например работа Корженёвского и Видмера 2017 года (arXiv:1706.02921). Сеть сама учится, какие признаки важны, и меньше путается с ударными и обертонами. Но учат её на разметке людей, а люди размечают глобальную тональность одной меткой. Поэтому вопрос «какая тональность у вещи, которая всё время модулирует» нейросеть не решает: она лучше угадывает ту же метку, которую поставил бы человек.
Я оставил в Nami классический метод: он работает без моделей, быстро и одинаково на сервере и на телефоне. Для подбора треков в микс этого хватает: соседние по кругу квинт тональности хорошо сочетаются, даже если метод ошибся на квинту.
Что из этого следует
-
Тональность от программы — ответ на вопрос «на какую мажорную или минорную гамму больше всего похож набор нот», а не музыкальный анализ.
-
Число корреляции полезнее названия. 0,85 — уверенный ответ. 0,3 — у трека нет одной ясной тональности.
-
Самые частые ошибки — квинта и родственная тональность. Их ждут даже правила конкурса MIREX.
Попробовать
-
Сайт: nami-music.ru
-
Android (8.0 и новее): скачать APK
-
Windows и Linux: скачать для компьютера
-
Сервер: инструкция по установке, ставится одной командой
Nami бесплатный, без рекламы и аккаунтов. Про проект целиком я рассказывал в первой статье.
Автор: AstolfoOwO


