Как преобразовать питч-трек из алгоритма извлечения мелодии в гудящий звуковой сигнал

В рамках исследовательского проекта «развлечение дома» я пытаюсь найти способ уменьшить / преобразовать песню в гудящий звуковой сигнал (основная мелодия, которую мы, люди, воспринимаем, когда слушаем песню). Прежде чем продолжить описание своей попытки решить эту проблему, я хотел бы упомянуть, что я совершенно не знаком с аудиоанализом, хотя у меня большой опыт анализа изображений и видео.

Немного погуглив, я нашел кучу алгоритмов извлечения мелодий. Учитывая полифонический звуковой сигнал песни (например, файл .wav), они выводят дорожку высоты тона - в каждый момент времени они оценивают доминирующую высоту звука (исходящую от голоса певца или какого-либо инструмента, генерирующего мелодию) и отслеживают доминирующую высоту звука. шаг с течением времени.

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

Я нашел два алгоритма:

  1. Отслеживание питча Yaapt
  2. Мелодия

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

«Это поколение этого гудения для любой произвольной песни, и мне нужна ваша помощь в этом вопросе».

Алгоритм (доступный как плагин vamp) выводит дорожку высоты тона --- [time_stamp, pitch / frequency] --- матрицу Nx2, где в первом столбце - отметка времени (в секундах), а во втором столбце - преобладающий тон. обнаружен на соответствующей отметке времени. Ниже показана визуализация дорожки основного тона, полученная с помощью алгоритма, наложенного фиолетовым цветом на сигнал во временной области песни (вверху) и его спектрограмму / кратковременный фурье. Отрицательные значения основного тона / частоты представляют собой алгоритм оценки доминирующего основного тона для неголосовых / немелодических сегментов. Так что все оценки высоты тона> = 0 соответствуют мелодии, остальные для меня не важны.

Наложение питч-трека с формой волны и спектрограммой песни

Теперь я хочу преобразовать этот питч-трек обратно в гудящий звуковой сигнал - точно так же, как это есть у авторов на своих сайтах.

Ниже приведена функция MATLAB, которую я написал для этого:

function [melSignal] = melody2audio(melody, varargin)
% melSignal = melody2audio(melody, Fs, synthtype)
% melSignal = melody2audio(melody, Fs)
% melSignal = melody2audio(melody)
%
% Convert melody/pitch-track to a time-domain signal
%
% Inputs:
%
%     melody - [time-stamp, dominant-frequency] 
%           an Nx2 matrix with time-stamp in the 
%           first column and the detected dominant 
%           frequency at corresponding time-stamp
%           in the second column. 
% 
%     synthtype - string to choose synthesis method
%      passed to synth function in synth.m
%      current choices are: 'fm', 'sine' or 'saw'
%      default='fm'
% 
%     Fs - sampling frequency in Hz 
%       default = 44.1e3
%
%   Output:
%   
%     melSignal -- time-domain representation of the 
%                  melody. When you play this, you 
%                  are supposed to hear a humming
%                  of the input melody/pitch-track
% 

    p = inputParser;
    p.addRequired('melody', @isnumeric);
    p.addParamValue('Fs', 44100, @(x) isnumeric(x) && isscalar(x));
    p.addParamValue('synthtype', 'fm', @(x) ismember(x, {'fm', 'sine', 'saw'}));
    p.addParamValue('amp', 60/127,  @(x) isnumeric(x) && isscalar(x));
    p.parse(melody, varargin{:});

    parameters = p.Results;

    % get parameter values
    Fs = parameters.Fs;
    synthtype = parameters.synthtype;
    amp = parameters.amp;

    % generate melody
    numTimePoints = size(melody,1);
    endtime = melody(end,1);
    melSignal = zeros(1, ceil(endtime*Fs));

    h = waitbar(0, 'Generating Melody Audio' );

    for i = 1:numTimePoints

        % frequency
        freq = max(0, melody(i,2));

        % duration
        if i > 1
            n1 = floor(melody(i-1,1)*Fs)+1;
            dur = melody(i,1) - melody(i-1,1);
        else
            n1 = 1;
            dur = melody(i,1);            
        end

        % synthesize/generate signal of given freq
        sig = synth(freq, dur, amp, Fs, synthtype);

        N = length(sig);

        % augment note to whole signal
        melSignal(n1:n1+N-1) = melSignal(n1:n1+N-1) + reshape(sig,1,[]);

        % update status
        waitbar(i/size(melody,1));

    end

    close(h);

end

Логика, лежащая в основе этого кода, следующая: на каждой временной отметке я синтезирую короткоживущую волну (скажем, синусоидальную волну) с частотой, равной обнаруженному доминирующему тону / частоте на этой временной отметке, в течение продолжительности, равной его пробел со следующей отметкой времени во входной матрице мелодии. Мне только интересно, правильно ли я это делаю.

Затем я беру аудиосигнал, получаемый от этой функции, и воспроизводю его с исходной песней (мелодия на левом канале и исходная песня на правом канале). Хотя сгенерированный аудиосигнал, кажется, достаточно хорошо сегментирует источники, генерирующие мелодию (голос / ведущий инструмент) - он активен там, где есть голос, и ноль везде - сам сигнал далеко не гудящий (я получаю что-то вроде beep beep beeeeep beep beeep beeeeeeeep), которые авторы показывают на своих сайтах. В частности, ниже представлена ​​визуализация, показывающая сигнал во временной области входной песни внизу и сигнал во временной области мелодии, сгенерированной с использованием моей функции.

введите описание изображения здесь

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

Есть ли у кого-нибудь предложения по этому поводу? Я приветствую предложения на любом языке программы (желательно MATLAB, python, C ++), но я предполагаю, что мой вопрос здесь более общий: как сгенерировать волну на каждой временной отметке?

Несколько идей / исправлений на мой взгляд:

  1. Установите амплитуду, получив усредненную / максимальную оценку амплитуды сигнала во временной области исходной песни.
  2. Полностью изменить мой подход - вычислить спектрограмму / кратковременное преобразование Фурье звукового сигнала песни. срезать с трудом / обнулить или мягко все остальные частоты, кроме тех, что в моем питч-треке (или близки к моему питч-треку). А затем вычислите обратное кратковременное преобразование Фурье, чтобы получить сигнал во временной области.

person cdeepakroy    schedule 17.03.2013    source источник
comment
Вы можете создать миди, имеющий те же высоты тона / изменения высоты звука / длительность / время, что и ваша мелодия, выбрать для него хороший инструмент и выполнить рендеринг в программе / библиотеке по вашему выбору. В качестве альтернативы, вы можете дать каждой ноте огибающую амплитуды, которая начинается с сильного (либо быстро нарастает от 0, либо начинается с сильного), уменьшается до тихой удерживаемой величины и затухает в конце. Это называется конвертом ADSR.   -  person Patashu    schedule 18.03.2013
comment
Почему-то мой новый тумбочка как домашний проект не так уж и впечатляет. -1 за то, что заставил меня почувствовать себя пещерным Игорем (шучу).   -  person Burhan Khalid    schedule 18.03.2013


Ответы (3)


Хотя у меня нет доступа к вашей функции synth (), исходя из требуемых параметров, я бы сказал, что ваша проблема в том, что вы не обрабатываете фазу.

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

Решение состоит в том, чтобы установить начальную фазу фрагмента n на конечную фазу фрагмента n-1. Вот пример того, как можно объединить две формы волны с разными частотами, не создавая разрыва фазы:

fs = 44100; % sampling frequency

% synthesize a cosine waveform with frequency f1 and starting additional phase p1
p1 = 0;
dur1 = 1;
t1 = 0:1/fs:dur1; 

x1(1:length(t1)) = 0.5*cos(2*pi*f1*t1 + p1);

% Compute the phase at the end of the waveform
p2 = mod(2*pi*f1*dur1 + p1,2*pi);

dur2 = 1;
t2 = 0:1/fs:dur2; 
x2(1:length(t2)) = 0.5*cos(2*pi*f2*t2 + p2); % use p2 so that the phase is continuous!

x3 = [x1 x2]; % this should give you a waveform without any discontinuities

Обратите внимание, что хотя это дает вам непрерывную форму волны, изменение частоты происходит мгновенно. Если вы хотите, чтобы частота постепенно изменялась с time_n на time_n + 1, вам придется использовать что-то более сложное, например, интерполяцию Маколея-Кватиери. Но в любом случае, если ваши фрагменты достаточно короткие, это должно звучать достаточно хорошо.

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

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

Надеюсь, что это ответ на ваш вопрос!

person jjs    schedule 18.03.2013
comment
@justin большое спасибо за решение. Это действительно была проблема, и после исправления все стало лучше. Но я чувствую, что мне нужно добавить лучшую амплитуду, чтобы быть более реалистичным, но это немного выходит за рамки моего вопроса в этом посте. Я читал, что воспринимаемая амплитуда зависит от частоты (чем выше частота, тем выше воспринимаемая амплитуда). Интересно, смогу ли я найти какую-нибудь математическую модель для этой зависимости, чтобы я мог изменять амплитуду на основе доминирующей высоты тона / частоты. Может так будет звучать даже лучше. - person cdeepakroy; 19.03.2013
comment
Кроме того, не могли бы вы подробнее рассказать об интерполяции Маколея-Кватиери или указать мне более простую статью - я все еще слышу некоторое щебетание в точке, где сигнал переходит от голосового к неголосому даже после применения некоторого сглаживания. - person cdeepakroy; 19.03.2013
comment
Изменение амплитуды синусоиды лишь незначительно повлияет на воспринимаемую реалистичность (это слово?) Синтезированного сигнала - он по-прежнему будет звучать как одна синусоида. Если вы хотите, чтобы он звучал как исходный источник, у вас есть два варианта: либо получить синтезатор для вашего источника (голос / инструмент) и использовать последовательность f0 для управления синтезом, либо использовать алгоритм разделения источников вместо алгоритма оценки f0. напрямую отделить сигнал от источника свинца (по крайней мере, попытаться сделать это, это все еще открытая исследовательская проблема). - person jjs; 19.03.2013
comment
Для разделения источников вы можете попробовать Melodyne (я думаю, есть бесплатная пробная версия) или код J.-L. Дурье, если вы ищете что-то с открытым исходным кодом: durrieu.ch/research/jstsp2010.html Интерполяция Маколея-Кватиери полезна только для интерполяции между двумя ненулевыми значениями частоты и амплитуды (a1, f1) - ›(a2, f2), она не поможет вам на границах между голосом / неголосым звуком. Для этого достаточно сгладить атаку. В любом случае, поскольку вы используете одну синусоиду (чисто тональную), атака никогда не будет звучать полностью естественно. - person jjs; 19.03.2013

Если я правильно понимаю, похоже, у вас уже есть точное представление о высоте звука, но ваша проблема в том, что то, что вы генерируете, просто «недостаточно хорошо звучит».

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

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

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

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

Наконец, я бы также поиграл с имеющимися у вас оценками длительности, чтобы у вас были более плавные переходы от одного звука к другому. Судя по тому, как вы исполнили свой аудиофайл, который мне очень понравился (beep beep beeeeep beep beeep beeeeeeeep), и по графику, который вы показываете, похоже, что у вас много прерываний, вставленных в рендеринг вашей песни. Вы можете избежать этого, увеличив оценку продолжительности, чтобы избавиться от тишины, которая короче, скажем, 0,1 секунды. Таким образом вы сохраните настоящую тишину из оригинальной песни, но не обрываете каждую ноту вашей песни.

person Lolo    schedule 18.03.2013
comment
Большое спасибо за вашу критику и предложения. Проблема заключалась в том, на что указал Саламон. Я не следил за тем, чтобы последовательные синусоидальные волны следовали непрерывно, создавая между ними резкий разрыв. Значит, сигнал не был обрядом звучания. После исправления звучит намного лучше. - person cdeepakroy; 19.03.2013
comment
Кстати, о замене синусоидальной волны более реалистичным сигналом с использованием банка выборок из реального инструмента --- доступен ли такой банк выборок или существуют математические модели для каких-либо инструментов. По-видимому, то, что определяет уникальность звука инструмента, - это обертоны или гармоники - целые числа, кратные основной частоте. Я читал, что это отношение амплитуд этих гармоник к основной частоте f, которая характеризует звук инструмента. Питч-трек кажется континуумом частот, а не отдельной нотой. - person cdeepakroy; 19.03.2013
comment
@cdeepakroy Да и нет. Да, эти соотношения, которые вы упомянули, обычно похожи для инструментов одного типа, но они все равно различаются в зависимости от конкретного инструмента, в пределах каждой ноты и от того, насколько сильно или мягко играется нота. И да, энергия имеет тенденцию накапливаться вокруг целых чисел, кратных основной частоте, но ни в коем случае не легко сводится к ним: эта иллюстрация говорит сама за себя: kozco.com/tech/audacity/piano_G1.jpg. Так что, если под рукой нет банка семплов, добавление гармоник определенно будет звучать лучше, но останется далеко от естественного звучания. - person Lolo; 19.03.2013
comment
@cdeepakroy И понял по поводу вашего первого комментария: резкие скачки - убийца. Я не видел (и до сих пор не вижу) проблемы, описанной в вашем исходном сообщении. Для записи многие инструменты редактирования звука решают этот подход с помощью другого подхода: вместо настройки фазы, что обычно невозможно, за исключением сигналов, близких к синусоиде, они используют перекрестное затухание (быстрое уменьшение амплитуды предыдущего сигнала и увеличение новый). - person Lolo; 19.03.2013

У вас как минимум 2 проблемы.

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

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

person hotpaw2    schedule 18.03.2013
comment
Предложение проанализировать спектр моего / человеческого гудения на одной тональной ноте было интересным. Но я считаю, что могу сделать это, вероятно, только для нот в паре октав и проанализировать их. Затем мне понадобится математическая модель, которая позволит мне получить (путем интерполяции или экстраполяции) сигнал на любой непрерывной частоте, которую, кажется, дает мне дорожка высоты тона алгоритма извлечения мелодии. Существует ли такая математическая модель? Я новичок в этой области, поэтому было бы полезно, если бы вы могли указать мне на такую ​​литературу. - person cdeepakroy; 19.03.2013
comment
Литература по вокальным формантам может охватывать некоторые модели, которые вы, возможно, захотите попробовать. - person hotpaw2; 19.03.2013