- PVSM.RU - https://www.pvsm.ru -
HOLO — приставка от греческого ὅλος, «весь».
Не без волнения рад представить вашему вниманию свою разработку, позволяющую объединять музыкальную библиотеку в единое целое с целью поиска «похожей» музыки.
Ещё несколько лет назад, на пике самостоятельного изучения MATLAB, мне захотелось создать программу, которая позволяла бы по заданному образцу музыки находить другие композиции «в том же духе». Куча уважительных причин заставляли откладывать реализацию всё дальше и дальше, но в какой-то момент дело сдвинулось с мёртвой точки. В результате, слегка изменив основу для разработки, первая версия программы была сделана.
Итак, постановка задачи выглядит следующим образом:
Платформой для разработки стал C#, как наиболее простой лично для меня язык, достаточно быстрый и имеющий хорошую среду разработки. Беглый поиск по Codeplex и подобным сайтам позволил избежать изобретения некоторых велосипедов, в частности используются следующие библиотеки:
Архитектурно программа состоит из двух подсистем – формирование базы данных о музыкальной коллекции и система поиска по базе данных.
Формирование базы данных реализовано следующим образом:
Анализируемый файл представляется в виде вектора из семплов, из которого извлекается N снимков (snap) длиной L семплов, каждый из которых приводится в АЧХ [6] (fft-snap) при помощи FFT-преобразования [7]. Это преобразование, по сути, превращает временной ряд в набор амплитуд разных частот, из которых состоит временной ряд. Затем fft-snap пропорционально сжимается в K раз (downscale ratio), чтобы уменьшить количество анализируемых в дальнейшем частот.
Далее каждая частота получившегося набора снимков рассматривается по-отдельности – рассчитываются статистические характеристики, такие как среднее значение (mean), медиана (median), стандартное отклонение (stdev), скос (skewness [8]) и крутизна (kurtosis [9]). В результате, для каждой композиции имеем набор из L/2K частот (FFT получается практически симметричным относительно середины), для каждой из которых рассчитаны 5 статистик. В базу данных кладётся имя файла, путь к нему на диске и 5L/2K вещественных чисел, каждое из которых помечено именем статистики, которое оно представляет.
Например, мы анализируем файл, сделав 40 снимков длиной в 64000 семплов, сжатых в 1000 раз. В итоге имеем вектор из 5*(64000/1000)/2 = 160 чисел для каждой композиции.
На этом основной этап формирования БД для анализа считаем завершённым. Вспомогательными шагами являются:
Теперь можно приступать к анализу. Простейший способ оценить близость двух точек в 160-мерном пространстве – посчитать евклидово расстояние между ними (сумма квадратов попарных разностей соответствующих статистик, квадратный корень не извлекаем — он удлиняет вычисления, но на результат сортировки не влияет). Казалось бы – бери и считай, но разные частоты в АЧХ композиции имеют разные разбросы и расчёт расстояния «в лоб» будет давать искажённое расстояние, с сильным акцентом на низкие частоты. Для того чтобы компенсировать нежелательный эффект, мы поделим квадрат разности между значениями на квадрат среднего значения амплитуды данной частоты. Таким образом, исходно анизотропное пространство характеристик распределения частот, в котором мы вычисляем расстояние, становится значительно более изотропным, то есть однородным.
Теоретически, можно провести кластерный анализ, объединяющий композиции по степени их близости, но пока было решено отказаться от применения этого метода, так как в его основе также лежит расчет расстояния между точками в пространстве (с выбором разных метрик), но алгоритм оказывается более сложным и не очень полезным в задаче сортировки композиций по возрастанию расстояния.
Пожалуй, теории пока достаточно, расскажу о практических результатах на основе моей коллекции музыки. Количество треков 30277, жанры самые разные – классическая музыка, рок, хард-рок, эмбиент, драм-н-бейс, хаус, техно, этника, весь спектр металла от лёгкого дума до самого ядерного и быстрого.
Анализ всей моей коллекции занял два с половиной дня, база данных без постобработки занимает 180 мегабайт, вместе с индексами — 460.
Интерфейс программы в части поиска заключается в динамическом формировании SQL-запроса, в котором реализован алгоритм расчёта евклидова расстояния для нормированных статистик по каждой частоте.
Имеется возможность указать как одну композицию в качестве образца, так и несколько. Вообще, возможность поиска песен, близких сразу к нескольким образцам, сильно увеличивает точность поиска, так как одна композиция может иметь паразитные вариации по статистикам, возникающие по разным причинам, но нивелируемые благодаря тому, что несколько образцов по-сути образуют центроид в многомерном пространстве, паразитные вариации которого сильно уменьшены. Главное при определении нескольких образцов — быть уверенным в том, что между образцами действительно имеется явная общность (например, композиции с одного альбома или в одном узком жанре и т.д.). Если образцы будут сильно отличающимися между собой, то выдача близких композиций будет нерелевантной.
Запрос при одном образце выполняется в среднем около минуты, при увеличении количества образцов время увеличивается пропорционально.
select b.id, sum(((a.statvalue - b.statvalue)*(a.statvalue - b.statvalue))/(c1.value*c1.value)) as distance,
10000*max((a.statvalue - b.statvalue)*(a.statvalue - b.statvalue)/(c2.value*c2.value)) as maxdiff,
10000*avg((a.statvalue - b.statvalue)*(a.statvalue - b.statvalue)/(c2.value*c2.value)) as meandiff
from stats a, stats b, feature c1, feature c2
where
a.id in (631, 1919) and b.id not in (631, 1919) and
a.statname = c1.statname and c1.feature = 'mean' and
a.statname = c2.statname and c2.feature = 'max_min' and
a.statname = b.statname and c1.statname = c2.statname and
a.statvalue <= b.statvalue + 75*c2.value/100 and a.statvalue >= b.statvalue - 75*c2.value/100 and
a.statname in ('b_median_FFT1_01', 'b_median_FFT1_02', 'b_median_FFT1_03', 'b_median_FFT1_04', 'b_median_FFT1_05', 'b_median_FFT1_06', 'b_median_FFT1_07', 'b_median_FFT1_08', 'b_median_FFT1_09', 'b_median_FFT1_10', 'b_median_FFT1_11', 'b_stdev_FFT1_01', 'b_stdev_FFT1_02', 'b_stdev_FFT1_03', 'b_stdev_FFT1_04', 'b_stdev_FFT1_05', 'b_stdev_FFT1_06', 'b_stdev_FFT1_07', 'b_stdev_FFT1_08', 'b_stdev_FFT1_09', 'b_stdev_FFT1_10', 'b_stdev_FFT1_11',
'b_skewn_FFT1_01', 'b_skewn_FFT1_02', 'b_skewn_FFT1_03', 'b_skewn_FFT1_04', 'b_skewn_FFT1_05', 'b_skewn_FFT1_06', 'b_skewn_FFT1_07', 'b_skewn_FFT1_08', 'b_skewn_FFT1_09', 'b_skewn_FFT1_10', 'b_skewn_FFT1_11', 'b_kurto_FFT1_01', 'b_kurto_FFT1_02', 'b_kurto_FFT1_03', 'b_kurto_FFT1_04', 'b_kurto_FFT1_05', 'b_kurto_FFT1_06', 'b_kurto_FFT1_07', 'b_kurto_FFT1_08', 'b_kurto_FFT1_09', 'b_kurto_FFT1_10', 'b_kurto_FFT1_11')
group by b.id
having maxdiff <= 100 and meandiff <= 64
order by distance limit 30;
В качестве примера, приведу результат работы запроса из трёх образцов песен, взятых с альбома “Songs of Darkness, Words of Light” группы My Dying Bride:
Эти композиции являются хорошим образцом жанра death-doom metal.
Плюс ещё 3 нерелевантных трека и 1 дубликат того что уже есть в списке. По-моему, уже достаточно приемлемо. Во всяком случае, я бы не отвергал такой плейлист, если бы захотелось послушать чего-то «в духе MDB».
Мог бы привести другие примеры, но в целом хочу сказать, что пока наиболее простым по идентификации жанром для программы является эмбиент (Aidan Baker, Tetsu Inoue, Lustmord и другие, тысячи их).
Конечно, они тоже есть, прежде всего из-за того что алгоритм сравнения далёк от совершенства.
Исходники выложил на Github [36]. (Кое-как удалось сделать пуш репозитория, первый раз в жизни, эхехех. Желающие помочь с выкладкой — прошу в ЛС, нужна небольшая консультация)
Для нежелающих возиться с гитхабом, выложил бинарники на гугл.драйв [37].
Прошу сильно строго не судить, делается всё на энтузиазме. Программирование на C# не является для меня основным профессиональным занятием.
Программа умеет сохранять и открывать плейлист, по двойному клику из списка образцов и списка результатов треки также можно открывать на прослушивание.
Если найдутся желающие помочь с оптимизацией SQL-запроса или формулированию дополнительных метрик кроме вышеописанных — пишите личные сообщения, буду рад сотрудничеству.
Если возникнут какие-либо ошибки при попытке запуска программы — тем более пишите, постараюсь помочь во всём.
Медиана частоты №10 из 31 против скоса

Медиана частоты №10 из 31 против стандартного отклонения

Крутизна частоты №10 из 31 против скоса (обычная зашумлённая парабола, не так ли?)

Гистограмма распределения среднего и медианы по левой оси и стандартного отклонения по правой

Пример среднего значения по всем частотам (делалось для базы по 100 частотам, отнормировано по среднему значению)

График сглаженного значения скоса по всем частотам (100 частот, отнормировано по среднему значению)
Автор: nervousnerd
Источник [38]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/muzy-ka/21600
Ссылки в тексте:
[1] NAudio: http://naudio.codeplex.com/
[2] Meta.Numerics: http://metanumerics.codeplex.com/
[3] SQLite: http://www.sqlite.org/index.html
[4] SQLite netFX wrapper: http://system.data.sqlite.org/index.html/doc/trunk/www/index.wiki
[5] ALGLIB: http://www.alglib.net/
[6] АЧХ: http://en.wikipedia.org/wiki/Frequency_response
[7] FFT-преобразования: http://en.wikipedia.org/wiki/FFT
[8] skewness: http://en.wikipedia.org/wiki/Skewness
[9] kurtosis: http://en.wikipedia.org/wiki/Kurtosis
[10] The Scarlet Garden: http://www.youtube.com/watch?v=Jeqr22MRsRw
[11] Catherine Blake: http://www.youtube.com/watch?v=lBXrx8C7uNU
[12] The Prize of Beauty: http://www.youtube.com/watch?v=H5vTGeJS6Tw
[13] F.U.B.A.R. — Crawling: http://www.youtube.com/watch?v=tLvrbhvlbBg
[14] The Soundbyte — The Dark: http://www.youtube.com/watch?v=yoyNlO4StVk
[15] AHAB — Ahabs Oath: http://www.youtube.com/watch?v=6z3no2pJfrk
[16] Gorement — Into Shadows: http://www.youtube.com/watch?v=Up5Q7MCYvDE
[17] Esoteric — Allegiance: http://www.youtube.com/watch?v=D1bkIy3Tk_4
[18] Autechre — Eutow: http://www.youtube.com/watch?v=8KsRIM4Jx4Q
[19] Entombed — Retaliation: http://www.youtube.com/watch?v=O7z_s-ZSSXo
[20] Nile — Divine Intent: http://www.youtube.com/watch?v=GliNPO_GdyQ
[21] Янтарные Слёзы — За Край Небес: http://www.youtube.com/watch?v=IJhNz23Covg
[22] NEUROSIS — Times of grace: http://www.youtube.com/watch?v=9RXZB7HxBRg
[23] Young Widows — In And Out Of Youth: http://www.youtube.com/watch?v=btmSpLVmJAk
[24] Empyrium — Autumn Grey Views: http://www.youtube.com/watch?v=V6zMGjnZR70
[25] Dark Tranquillity — Hours Passed In Exile: http://www.youtube.com/watch?v=vZKPuV34tso
[26] Shape of Despair – Quiet These Paintings Are: http://www.youtube.com/watch?v=yEe5UuvL2lo
[27] Rosetta — A Determinism of Morality: http://www.youtube.com/watch?v=TJBEaoNJz1o
[28] Solstafir – Ghosts of Light: http://www.youtube.com/watch?v=IvsBilNQMIg
[29] Led Zeppelin — Ten Years Gone: http://www.youtube.com/watch?v=jYpydtdlWxA
[30] Voivod — Divine Sun: http://www.youtube.com/watch?v=G4_e8GFTSRo
[31] Cradle of Filth — Malice Through The Looking Glass: http://www.youtube.com/watch?v=-N9PMRAu-Ak
[32] Suffocation — Redemption: http://www.youtube.com/watch?v=gSr3Cd1OqT0
[33] Nihilist –Abnormally Deceased: http://www.youtube.com/watch?v=AuB2xSRLuEw
[34] Reverend Bizarre – The Gate of Nanna: http://www.youtube.com/watch?v=btjqOtsg7N0
[35] Центральные моменты N-го порядка: http://en.wikipedia.org/wiki/Moment_about_the_mean
[36] Github: https://github.com/johnnybuggy/holo
[37] бинарники на гугл.драйв: https://docs.google.com/open?id=0B07oznb_RwUNdnZSVS1xNi1TVXM
[38] Источник: http://habrahabr.ru/post/161005/
Нажмите здесь для печати.