自宅で楽しむ研究プロジェクトの一環として、私は曲を音声信号のようなハミング(曲を聴いたときに人間が知覚する基本的なメロディー)に変換/変換する方法を見つけようとしています。この問題の試みについて説明する前に、私は画像やビデオの分析に多くの経験がありますが、オーディオ分析にはまったく慣れていないことを述べておきます。
少しグーグルした後、私はたくさんのメロディー抽出アルゴリズムを見つけました。曲のポリフォニックオーディオ信号(例:.wavファイル)が与えられると、ピッチトラックを出力します---各時点で、支配的なピッチ(歌手の声またはメロディー生成機器から来る)を推定し、支配的なピッチを追跡します時間の経過とともにピッチします。
私はいくつかの論文を読みましたが、それらは曲の短時間フーリエ変換を計算し、スペクトログラムで分析を行って支配的なピッチを取得して追跡しているようです。メロディー抽出は、私が開発しようとしているシステムのコンポーネントにすぎないため、オーディオファイルで適切な処理を実行し、コードが利用可能である限り、利用可能なアルゴリズムを使用してもかまいません。私はこれに慣れていないので、どのアルゴリズムがうまく機能することがわかっているか、そしてそのコードをどこで見つけることができるかについての提案を聞いてうれしいです。
私は2つのアルゴリズムを見つけました:
さまざまな音楽ジャンルの結果が非常に印象的だったので、私はMelodiaを選びました。結果を確認するには、これを確認してください。曲ごとに聞こえるハミングは、基本的に私が興味を持っているものです。
「この質問であなたの助けを求めているのは、任意の曲に対するこのハミングの生成です」。
アルゴリズム(vampプラグインとして利用可能)はピッチトラックを出力します--- [time_stamp、pitch / frequency] --- Nx2マトリックス。最初の列はタイムスタンプ(秒単位)で、2番目の列はドミナントピッチです。対応するタイムスタンプで検出されます。以下に示すのは、曲の時間領域信号(上)と紫色でオーバーレイされたアルゴリズムから得られたピッチトラックの視覚化であり、スペクトログラム/短時間フーリエです。負の値-ピッチ/周波数の値は、無声/非メロディックセグメントのアルゴリズムの支配的なピッチ推定を表します。したがって、すべてのピッチ推定値> = 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)著者が自分のWebサイトに表示します。具体的には、下の入力曲の時間領域信号と、私の関数を使用して生成されたメロディーの時間領域信号を示す視覚化です。
主な問題の1つは、各タイムスタンプで生成される波の周波数と持続時間が与えられているにもかかわらず、波の振幅を設定する方法がわからないことです。今のところ、振幅をフラット/一定の値に設定しましたが、これが問題の原因であると思われます。
誰かがこれについて何か提案がありますか?任意のプログラム言語(MATLAB、Python、C ++が望ましい)での提案を歓迎しますが、ここでの私の質問はより一般的だと思います---各タイムスタンプでウェーブを生成する方法は?
私の心の中のいくつかのアイデア/修正:
- 元の曲の時間領域信号から振幅の平均/最大推定値を取得して、振幅を設定します。
- 私のアプローチを完全に変えてください---曲のオーディオ信号のスペクトログラム/短時間フーリエ変換を計算してください。ピッチトラック内の周波数(またはピッチトラックに近い周波数)を除く他のすべての周波数をほとんど/ゼロアウトまたはソフトにカットオフします。次に、逆短時間フーリエ変換を計算して、時間領域信号を取得します。