ESPN Deportes¡EN VIVO! Colombia enfrenta a Paraguay en un nuevo amistoso de la fecha FIFAPunchEx-CAN president asks Tinubu to tackle hunger, insecurityThe Jerusalem PostNoah’s Ark? Researchers claim radar scan reveals 'man-made' structure at Turkey siteCNN TürkArnavutköy'de seyir halindeki TIR, otobüs durağına daldıColliderMark Wahlberg’s New Crime Thriller Officially Loses Nearly 3,000 TheatersZDF heuteAktuelle Pressemitteilungen des ZDFUOLJustiça determina prisão de bombeiro que matou estudante de medicina em Taubaté (SP)경향신문“당 떨어졌나?” 초콜릿부터 찾았는데…연구 결과 보니 오히려 더 피곤해져GIGAZINEAppleがAIエージェントのリスクを鑑みMacのフルディスクアクセスに制限を設けるRapplerMeet Sarah Jane Lim, the mother leading Philippine pickleballESPNPenguins' Kuzmenko to wear No. 62 to honor dad killed in bear attackSportstarAsian Games 2026 Live Updates Day 15: India's Kumkum in action in recurve archery final; Pranavi Urs second in golf; cricket score and wrestling results
The Daily Newsstand · Free, Always
Saturday, October 3, 2026

Как плеер угадывает тональность трека: 12 чисел и одна корреляция

Translate

Я делаю музыкальный плеер 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 бесплатный, без рекламы и аккаунтов. Про проект целиком я рассказывал в первой статье.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.