Подтвердить что ты не робот

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

В рамках проекта "fun-at-home-research-project" я пытаюсь найти способ уменьшить/преобразовать песню в гудение, как аудиосигнал (основная мелодия, которую мы, люди, воспринимаем, когда слушаем песню). Прежде чем я расскажу о своей попытке решить эту проблему, я хотел бы упомянуть, что я совершенно не знаком с аудиоанализом, хотя у меня есть большой опыт анализа изображений и видео.

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

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

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

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

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

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

Pitch-track overlay with a song's waveform and spectrogram

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

Ниже приведена функция 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

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

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

enter image description here

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

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

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

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

Ответ 1

Хотя у меня нет доступа к вашей функции 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, вам нужно было бы использовать что-то более сложное, как интерполяция McAulay-Quatieri. Но в любом случае, если ваши фрагменты достаточно короткие, это должно звучать достаточно хорошо.

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

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

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

Ответ 2

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

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

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

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

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

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

Ответ 3

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

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

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