自从我提出这个问题以来已经有一段时间了,在对这个主题做了一些工作之后,我认为是时候重新审视它了。应该注意的是,虽然光谱质心与任何其他光谱特征非常相似,是使用 FFT 幅度而不是原始 FFT 数据计算的,但我们不必直接操纵幅度。相反,我们修改 FFT 数据,这反过来会影响幅度。这使得该过程比必须操纵幅度要容易得多。
光谱质心是单个值(每个 FFT 帧),因此在 STFT 的情况下,我们拥有与 STFT 帧一样多的质心。操纵光谱质心的过程非常简单。例如,如果频谱质心为 600Hz,并且我们希望将其值增加 10% 以使新值为 660Hz,我们只需将频率 bin 重新排列一定数量(bin step)。实际计算很简单
const binStep = frameLenHalf - frameLenHalf/(1+(float)freqDelta/100);
//frequency delta % (e.g. 10%, 20%, -10%, -20% etc)
//for increasing frequency
float *pCfftLReal = new float[frameLen];
float *pCfftLImg = new float[frameLen];
//for increasing frequency
for(j=frameLenHalf-1;j>binStep;j--){
pCfftLReal[j] = pFFTReal0[j-binStep];
pCfftLImg[j] = pFFTImg0[j-binStep];
}
}
//for decreasing frequency
for(int j=0; j<frameLenHalf-1-binStep; j++){
pCfftLReal[j] = pFFTReal0[j+binStep];
pCfftLImg[j] = pFFTImg0[j+binStep];
}
不用说,这个过程存在舍入误差(主要是计算 binStep),但一般来说,bin 分辨率越大,误差越大。这可以通过具有更大的帧长度来缓解(但对于 STFT,这通常是不可取的)。其他更复杂的方法也是可能的,但我没有研究过这些方法。
此外,如果频移量超过 Nyqiust 频率并且我们保持原始采样率,有时结果可能会出现偏差(上采样在这里会有所帮助)。
在我们真正听到我们操纵的声音之前,应该对我们修改的数据执行逆 FFT。
我在这里描述的只是一种粗略、幼稚的光谱质心操作方法。还应该说,这种操作不会保留原始谐波比,因为频率增量偏移在每个 bin 上均等应用。保持谐波比不变将需要为每个频率计算一个新的 bin 步长,这仅比上面给出的算法稍微复杂一些。
我已经尝试了这两种方法(使用恒定的 bin 步长和不断变化的 bin 步长),并且在感知上,第一个并没有真正改变原始声音(我想这与高频影响音色有关,而不是低频和第一个方法对较高频率的改变小于对较低频率的改变)。
我还没有完成这个,所以一旦我有有趣的事情要分享,我会更新这篇文章。