我使用 apache commons 数学库来转换我的音频样本缓冲区上的 FFt 和 IFFT。FFT 的输出给了我一个复数数组。频率反映在中间。使用 4096 个样本的样本缓冲区大小,我得到 2048 个有用的复数。
我在 Java 中有两种实现,一种在 IFFT 之前遍历最终数组,并计算应该从中获取复数的位置的插值。所以基本上我正在做的是在另一个频率范围内扭曲复数。
FastFourierTransformer fft = new FastFourierTransformer(DftNormalization.STANDARD);
Complex[] freq, inverse, freqn;
for(int c = 0; c < in.length; c++){
freq = fft.transform(in[c], TransformType.FORWARD);
freqn = new Complex[freq.length];
freqn[0] = Complex.valueOf(freq[0].getReal(), freq[0].getImaginary());
for (int i = 1; i <= freq.length/2; i++) {
double fOrig = i / factor + shift;
int left = (int) Math.floor(fOrig);
int right = (int) Math.ceil(fOrig);
double weighting = fOrig - left;
double new_Re = 0, new_Im = 0;
if(left > 0 && left < freq.length / 2 && right > 0 && right < freq.length / 2){
new_Re = interpolate(freq[left].getReal(), freq[right].getReal(), weighting);
new_Im = interpolate(freq[left].getImaginary(), freq[right].getImaginary(), weighting);
}
freqn[i] = Complex.valueOf(new_Re, new_Im);
freqn[freq.length-i] = Complex.valueOf(new_Re, new_Im);
}
inverse = fft.transform(freqn, TransformType.INVERSE);
for(int i = 0; i < inverse.length; i++){
in[c][i] = inverse[i].getReal();
}
}
由于输入音频信号的采样率,我从一个音高频率中获得了多个音高频率,因此这种实现主要在高音区域产生具有副作用的声音。我的其他实现计算传入复数的幅度和相位。然后它仅将幅度标度扭曲到新位置,然后在原始相位值和新幅度值的帮助下计算新复数。在矩形到极坐标和矩形之间转换时,我失去了我的标志。因为我只改变了复数向量的长度,所以我可以在输出复数上强制输入符号。
FastFourierTransformer fft = new FastFourierTransformer(DftNormalization.STANDARD);
Complex[] freq, inverse;
for(int c = 0; c < in.length; c++){
freq = fft.transform(in[c], TransformType.FORWARD);
double[] ampl = new double[freq.length];
double[] angl = new double[freq.length];
double re, im;
boolean[] unitRe = new boolean[freq.length];
boolean[] unitIm = new boolean[freq.length];
double fctr = factor;
for(int f = 0; f < freq.length; f++){
re = freq[f].getReal();
im = freq[f].getImaginary();
unitRe[f] = re >= 0;
unitIm[f] = im >= 0;
ampl[f] = op.magn(re, im);
angl[f] = op.agl(re, im);
}
for(int f = 0; f < freq.length; f++){
int val = f < freq.length / 2 ? f : freq.length / 2 - (f - freq.length / 2);
double weighting = ((double)val / fctr + shift) % 1;
int left = (int) Math.floor(val / fctr + shift);
int right = (int) Math.ceil(val / fctr + shift);
double new_ampl = 0;
if(left >= 0 && left < freq.length / 2 && right >= 0 && right < freq.length / 2){
new_ampl = interpolate(ampl[left], ampl[right], weighting);
}
re = op.real(new_ampl, angl[f]);
im = op.imag(new_ampl, angl[f]);
re = unitRe[f] ? Math.abs(re) : Math.abs(re) * -1;
im = unitIm[f] ? Math.abs(im) : Math.abs(im) * -1;
freq[f] = Complex.valueOf(re, im);
}
inverse = fft.transform(freq, TransformType.INVERSE);
for(int i = 0; i < inverse.length; i++){
in[c][i] = inverse[i].getReal();
}
}
第二个实现听起来比第一个好得多。它实际上甚至比我使用的大多数 Dj 应用程序听起来更好,但我不知道为什么?难道我做错了什么?我在 Java 中找不到任何其他实现可以比较。他们通常只是用幅度和相位在一个新的尺度上扭曲整个频率尺度,还是他们只是把幅度和强迫它放到另一个尺度的原始相位上?