我一直在使用 Rosetta Code 提供的原始 FFT 算法开发频移器。理解到,为了对样本信号进行频移,需要将原始音频应用 FFT,将每个结果正弦波的频率乘以频移因子(由用户定义),然后将正弦波加在一起。当我运行算法时,输出的质量非常低,就好像算法中收集到的正弦波数量不足以正确地再现信号。该算法在头文件中实现为类,并在其他地方(正确地)调用。
#include <complex>
#include <valarray>
typedef std::complex<double> Complex;
typedef std::valarray<Complex> CArray;
class FrequencyShifter {
float sampleRate;
public:
FrequencyShifter() {
}
void setSampleRate(float inSampleRate) {
sampleRate = inSampleRate;
}
double abs(double in0) {
if (in0>=0) return in0;
else return -in0;
}
void fft(CArray& x)
{
const size_t N = x.size();
if (N <= 1) return;
// divide
CArray even = x[std::slice(0, N/2, 2)];
CArray odd = x[std::slice(1, N/2, 2)];
// conquer
fft(even);
fft(odd);
// combine
for (size_t k = 0; k < N/2; ++k)
{
Complex t = std::polar(1.0, -2 * PI * k / N) * odd[k];
x[k ] = even[k] + t;
x[k+N/2] = even[k] - t;
}
}
double convertToReal(double im, double re) {
return sqrt(abs(im*im - re*re));
}
void processBlock(float *inBlock, const int inFramesToProcess, float scale) {
//inFramesToProcess is the amount of samples in inBlock
Complex *copy = new Complex[inFramesToProcess];
for (int frame = 0; frame<inFramesToProcess; frame++) {
copy[frame] = Complex((double)inBlock[frame], 0.0);
}
CArray data(copy, inFramesToProcess);
fft(data);
const float freqoffsets = sampleRate/inFramesToProcess;
for (float x = 0; x<data.size()/2; x++) {
for (float frame = 0; frame<inFramesToProcess; frame++) {
inBlock[(int)frame] = (float)(convertToReal(data[(int)x].imag(), data[(int)x].real())*sin(freqoffsets*x*frame*scale));
}
}
}
};
我猜问题的一部分在于我只包括sampleRate/inFramesToProcess
频率来覆盖正弦波。发送更大的音频文件(因此更大的*inBlock
和inFramesToProcess
)会使音频变得不那么颗粒状吗?我该如何做到这一点而不仅仅改变参数的值或长度?
*inBlock
的输出时,没有电平(音频电平为0或遇到其他错误)。基本上,算法中存在一些错误,我无法检测和修复。 - Linus Rastegar