回答此问题可获得 20 贡献值,回答如果被采纳可获得 50 分。
<p>作为一个有趣的家庭研究项目的一部分,我正试图找到一种方法来减少/转换一首歌成一种类似嗡嗡声的音频信号(我们人类在听一首歌时感知到的潜在旋律)。在我进一步描述我在这个问题上的尝试之前,我想说我对音频分析是全新的,尽管我在分析图像和视频方面有很多经验。在</p>
<p>在谷歌搜索了一下之后,我发现了一堆旋律提取算法。给定一首歌的复调音频信号(例如:.wav文件),他们输出一个音调轨迹——在每个时间点,他们估计主音高(来自歌手的声音或某些旋律生成乐器),并随时间跟踪主音高。在</p>
<p>我读了几篇论文,他们似乎在计算歌曲的短时傅里叶变换,然后对频谱图做一些分析,以得到并跟踪主音的音调。旋律提取只是我正在开发的系统中的一个组件,所以我不介意使用任何可用的算法,只要它对我的音频文件做得很好,代码是可用的。由于我是新手,我很乐意听到任何关于哪些算法可以很好地工作以及在哪里可以找到它的代码的建议。在</p>
<p>我发现了两种算法:</p>
<ol>
<li><a href="http://www.ws.binghamton.edu/zahorian/yaapt.htm" rel="noreferrer">Yaapt pitch tracking</a></li>
<li><a href="http://mtg.upf.edu/technologies/melodia" rel="noreferrer">Melodia</a></li>
</ol>
<p>我选择了Melodia,因为不同音乐类型的结果看起来相当令人印象深刻。请检查<a href="http://www.justinsalamon.com/melody-extraction.html#demo" rel="noreferrer">this to see its results</a>。你听到的每一段音乐的嗡嗡声本质上就是我感兴趣的。在</p>
<p>“这是这一代哼唱任意歌曲,我希望你在这个问题上的帮助”。在</p>
<p>算法(可用作vamp插件)输出一个音调轨迹---[time_stamp,pitch/frequency]——一个Nx2矩阵,其中第一列是时间戳(以秒为单位),第二列是在相应的时间戳处检测到的主音高。下面显示的是从算法获得的音调轨迹的可视化显示,该算法用紫色覆盖歌曲的时域信号(上图)及其频谱图/短时傅里叶。基音/频率的负值表示非浊音/非旋律段的主要基音估计算法。所以所有的音高估计值>;=0与旋律相对应,其余的对我来说并不重要。在</p>
<p><img src="https://i.stack.imgur.com/MKF3V.png" alt="Pitch-track overlay with a song's waveform and spectrogram"/></p>
<p>现在我想把这个音高轨迹转换成类似嗡嗡声的音频信号——就像作者在他们的网站上看到的那样。在</p>
<p>下面是我编写的一个MATLAB函数:</p>
<pre><code>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
</code></pre>
<p>这段代码背后的逻辑是:在每个时间戳,我合成一个短波(比如正弦波),频率等于在该时间戳检测到的主音节/频率,持续时间等于它与输入旋律矩阵中下一个时间戳的间隔。我只想知道我是否做得对。在</p>
<p>然后,我从这个函数中获取音频信号,并与原始歌曲一起播放(左声道的旋律和右声道的原始歌曲)。虽然生成的音频信号似乎很好地分割了产生旋律的源(声音/导联乐器),但它在声音所在的地方是活跃的,而其他地方都是零的——信号本身远不是作者在他们的网站上显示的嗡嗡声(我得到了一些类似于哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔哔。具体地说,下面是一个可视化显示,在底部输入歌曲的时域信号和使用我的函数生成的旋律的时域信号。在</p>
<p><img src="https://i.stack.imgur.com/dfSeU.png" alt="enter image description here"/></p>
<p>一个主要问题是——虽然我得到了在每个时间戳产生的波的频率和持续时间,但我不知道如何设置波的振幅。现在,我将振幅设置为平坦/恒定值,我怀疑这就是问题所在。在</p>
<p>有人对此有什么建议吗?我欢迎任何程序语言的建议(最好是Matlab、Python、C++),但我想我这里的问题更一般——如何在每一个时间戳上产生波?在</p>
<p>我的一些想法/解决方法:</p>
<ol>
<li>通过从原始歌曲的时域信号中获得振幅的平均/最大估计值来设置振幅。在</li>
<li>完全改变我的方法——计算歌曲音频信号的频谱图/短时傅里叶变换。几乎不切断/调零或柔和地切断所有其他频率,除了在我的音高轨迹(或接近我的音高轨迹)。然后计算短时傅里叶逆变换得到时域信号。在</li>
</ol>