Вопрос

Я разрабатываю систему в помощь музыкантам, выполняющим транскрипцию. Цель состоит в том, чтобы выполнить автоматическую транскрипцию музыки (она не должна быть идеальной, так как пользователь исправит ошибки / ошибки позже) на монофонической записи одного инструмента. У кого-нибудь здесь есть опыт автоматической транскрипции музыки? Или цифровая обработка сигналов вообще? Любая помощь очень ценится независимо от вашего происхождения.

До сих пор я исследовал использование быстрого преобразования Фурье для обнаружения основного тона, и ряд тестов как в MATLAB, так и в моих собственных программах тестирования Java показали, что он достаточно быстрый и точный для моих нужд. Другим элементом задачи, которую необходимо решить, является отображение произведенных MIDI-данных в виде нот, но это то, что меня сейчас не касается.

Короче говоря, я ищу хороший метод для обнаружения начала ноты, то есть положение в сигнале, где начинается новая нота. Поскольку медленное начало может быть довольно трудно обнаружить должным образом, я сначала буду использовать систему с записями на фортепиано. Это также частично связано с тем, что я играю на пианино и должен быть в лучшем положении, чтобы получить подходящие записи для тестирования. Как указывалось выше, ранние версии этой системы будут использоваться для простых однотонных записей, возможно, переходя позже к более сложному вводу в зависимости от прогресса, достигнутого в ближайшие недели.

Это было полезно?

Решение

Вот рисунок, который иллюстрирует пороговый подход к обнаружению появления заметки:

alt text

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

Как показывает изображение, выбрать правильный абсолютный порог сложно. В этом случае первая нота подобрана нормально, вторая нота полностью пропущена, а третья (едва) начинается очень поздно. В общем, низкий порог заставляет вас выбирать фантомные ноты, а при его повышении вы пропускаете ноты. Одним из решений этой проблемы является использование относительного порога, который запускает запуск, если сигнал увеличивается на определенный процент в течение определенного времени, но это имеет свои собственные проблемы.

Более простое решение состоит в том, чтобы сначала использовать в вашем волновом файле сжатое с несколько противоречивым названием сжатие ( не MP3-сжатие - это что-то совсем другое ). Сжатие по существу сглаживает пики в ваших аудиоданных, а затем усиливает все, так что большее количество звука приближается к максимальным значениям. Эффект на приведенном выше примере будет выглядеть следующим образом (это показывает, почему имя & Quot; сжатие & Quot; кажется, не имеет смысла - на аудиооборудовании это обычно помечается как & Quot; loudness & Quot; ):

alt text

После сжатия подход с абсолютным порогом будет работать намного лучше (хотя его легко сжимать и начинать подбирать вымышленную ноту, тот же эффект, что и при снижении порога). Существует множество волновых редакторов, которые хорошо справляются со сжатием, и лучше позволить им справиться с этой задачей - вам, вероятно, потребуется проделать большую работу & Quot; очистка & Quot ; ваши волновые файлы, прежде чем обнаруживать в них заметки.

В терминах кодирования WAV-файл, загруженный в память, представляет собой массив двухбайтовых целых чисел, где 0 обозначает отсутствие сигнала, а 32,767 и -32,768 обозначают пики. В своей простейшей форме алгоритм обнаружения порога будет просто запускаться с первой выборки и считывать массив, пока не найдет значение, превышающее порог.

short threshold = 10000;
for (int i = 0; i < samples.Length; i++)
{
    if ((short)Math.Abs(samples[i]) > threshold) 
    {
        // here is one note onset point
    }
}

На практике это работает ужасно, поскольку нормальное аудио имеет всевозможные переходные пики выше заданного порога. Одно из решений состоит в том, чтобы использовать текущую среднюю мощность сигнала (т.е. не отмечать начало, пока среднее значение из последних n выборок не превысит пороговое значение).

short threshold = 10000;
int window_length = 100;
int running_total = 0;
// tally up the first window_length samples
for (int i = 0; i < window_length; i++)
{
    running_total += samples[i];
}
// calculate moving average
for (int i = window_length; i < samples.Length; i++)
{
    // remove oldest sample and add current
    running_total -= samples[i - window_length];
    running_total += samples[i];
    short moving_average = running_total / window_length;
    if (moving_average > threshold)
    {
        // here is one note onset point 
        int onset_point = i - (window_length / 2);
    }
}

Все это требует значительных изменений и настройки параметров, чтобы он мог точно находить начальные позиции файла WAV, и обычно то, что работает для одного файла, не очень хорошо работает для другого. Это очень сложная и не совсем решенная проблемная область, которую вы выбрали, но я думаю, что это здорово, что вы решаете ее.

Обновление: на этом рисунке показаны подробности обнаружения заметки, которые я пропустил, а именно: определение окончания заметки:

alt text

Желтая линия представляет собой пороговое значение. Как только алгоритм обнаружил начало ноты, он предполагает, что нота продолжается до тех пор, пока текущее среднее значение сигнала не упадет ниже этого значения (показано здесь фиолетовыми линиями). Это, конечно, еще один источник трудностей, как в случае, когда два или более примечаний перекрываются (полифония).

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

Обновление 2: я только что прочитал ваш обновленный вопрос. Обнаружение основного тона с помощью автокорреляции гораздо проще реализовать, чем FFT, если вы пишете свое собственное с нуля, но если вы уже извлекли и использовали предварительно созданную библиотеку FFT, вылучше использовать его наверняка. После того, как вы определили начальную и конечную позиции каждой ноты (и включили некоторые отступы в начале и в конце для пропущенных частей атаки и выпуска), теперь вы можете извлечь каждый фрагмент аудиоданных и передать их функции FFT для определить высоту тона.

Одним из важных моментов здесь является не использование фрагмента сжатых аудиоданных, а использование фрагмента исходных неизмененных данных. Процесс сжатия искажает звук и может привести к неточному считыванию основного тона.

И последнее замечание о времени атаки на ноты: это может быть меньшей проблемой, чем вы думаете. Часто в музыке инструмент с медленной атакой (например, мягкий синтезатор) начинает ноту раньше, чем острый инструмент атаки (например, пианино), и обе ноты будут звучать так, как если бы они начинались одновременно. Если вы играете на инструментах подобным образом, алгоритм выбирает одинаковое время начала для обоих типов инструментов, что хорошо с точки зрения WAV-MIDI.

Последнее обновление (надеюсь). Забудьте о том, что я сказал о включении некоторых образцов отступов из части ранней атаки каждой заметки - я забыл, что на самом деле это плохая идея для определения высоты тона. Части атаки многих инструментов (особенно пианино и других инструментов ударного типа) содержат переходные процессы, которые не кратны основной высоте, и имеют тенденцию портить определение высоты звука. По этой причине вы действительно хотите начинать каждый срез немного после атаки.

Да, и важно: термин " сжатие " здесь не упоминается сжатие в стиле MP3 .

Обновите снова: вот простая функция, которая выполняет динамическое сжатие:

public void StaticCompress(short[] samples, float param)
{
    for (int i = 0; i < samples.Length; i++)
    {
        int sign = (samples[i] < 0) ? -1 : 1;
        float norm = ABS(samples[i] / 32768); // NOT short.MaxValue
        norm = 1.0 - POW(1.0 - norm, param);
        samples[i] = 32768 * norm * sign;
    }
}

Когда param = 1.0, эта функция не будет влиять на звук. Большие значения параметра (2,0 - это хорошо, что будет возводить в квадрат нормализованную разницу между каждым сэмплом и максимальным пиковым значением), что приведет к большей компрессии и более громкому (но дрянному) звучанию. Значения ниже 1.0 создадут эффект расширения.

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

Обновление: вот версия StaticCompress, которая будет компилироваться в C #, и Explicity приведёт все. Это возвращает ожидаемый результат:

public void StaticCompress(short[] samples, double param)
{
    for (int i = 0; i < samples.Length; i++)
    {
        Compress(ref samples[i], param);
    }
}

public void Compress(ref short orig, double param)
{
    double sign = 1;
    if (orig < 0)
    {
        sign = -1;
    }
    // 32768 is max abs value of a short. best practice is to pre-
    // normalize data or use peak value in place of 32768
    double norm = Math.Abs((double)orig / 32768.0);
    norm = 1.0 - Math.Pow(1.0 - norm, param);
    orig = (short)(32768.0 * norm * sign); // should round before cast,
        // but won't affect note onset detection
}

Извините, мой уровень знаний о Matlab равен 0. Если вы разместили еще один вопрос о том, почему ваша функция Matlab не работает должным образом, на нее ответят (только не я).

Другие советы

То, что вы хотите сделать, часто называется WAV-to-MIDI (google " wav-to-midi "). Было много попыток этого процесса, с разными результатами (заметка - одна из трудностей; с полифонией гораздо сложнее иметь дело). Я бы порекомендовал начать с тщательного поиска готовых решений и начинать работу самостоятельно, только если нет ничего приемлемого.

Другая часть процесса, который вам понадобится, это что-то, чтобы сделать MIDI-вывод традиционной музыкальной партитурой, но есть миллионы продуктов, которые это делают.

Другой ответ: да, я много занимался цифровой обработкой сигналов (см. программное обеспечение на моем веб-сайте - это программный синтезатор с бесконечным голосом, написанный на VB и C), и я заинтересован в том, чтобы помочь вам с Эта проблема. Концептуально часть WAV-to-MIDI не так уж и сложна, она просто делает ее надежной на практике, что сложно. Начало записи - это просто установка порога - ошибки могут быть легко скорректированы вперед или назад во времени, чтобы компенсировать различия в атаках нот. Обнаружение основного тона на записи гораздо проще, чем в режиме реального времени, и включает в себя просто выполнение процедуры автокорреляции.

Вам следует взглянуть на MIRToolbox - он написан для Matlab и имеет встроенный детектор начала - он работает довольно хорошо. Исходный код GPL, так что вы можете реализовать алгоритм на любом языке, который вам подходит. Какой язык будет использовать ваш производственный код?

эта библиотека сосредоточена вокруг звуковой маркировки:

aubio

  

aubio - это библиотека для звуковой маркировки. Его функции включают сегментирование звукового файла перед каждой из его атак, выполнение определения высоты тона, нажатие на удар и создание миди-потоков из живого аудио. Название aubio происходит от «audio» с опечаткой: в результатах также могут быть обнаружены несколько ошибок транскрипции.

и мне повезло с этим для обнаружения начала и определения высоты тона. Это в c, но есть обертки swig / python.

Кроме того, у автора библиотеки есть страница с диссертацией на этой странице, которая содержит отличную информацию и информацию о маркировке.

Твердые приступы легко обнаруживаются во временной области с помощью измерения средней энергии.

СУММА от 0 до N (X ^ 2)

Сделайте это с кусками всего сигнала. Вы должны видеть пики, когда происходят вхождения (размер окна зависит от вас, мое предложение составляет 50 мс или более).

Обширные документы по обнаружению начала:

Для хардкорных инженеров:

http://www.nyu.edu/classes/bello/MIR_files /2005_BelloEtAl_IEEE_TSALP.pdf

Обычному человеку легче понять:

http://bingweb.binghamton.edu/~ahess2/Onset_Detection_Nov302011.pdf< а>

Вы можете попытаться преобразовать wav-сигнал в график зависимости амплитуды от времени. Тогда способ определения согласованного начала состоит в том, чтобы рассчитать пересечение касательной в точке перегиба восходящего фланга сигнала с осью x.

scroll top