オーディオ サンプル バッファで FFT と IFFT を変換するために、apache commons math ライブラリを使用します。FFT の outout は、複素数の配列を提供します。周波数は中央に反映されます。サンプル バッファ サイズが 4096 サンプルの場合、2048 の有用な複素数が得られます。
Java には 2 つの実装があります。1 つは 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();
}
}
この実装では、入力オーディオ信号のサンプルレートにより、複数のピッチ周波数が 1 つから得られるため、主に高域で副作用のあるサウンドのピッチが設定されます。私の他の実装では、受信する複素数の振幅とフェーズ アウトを計算します。次に、振幅スケールのみを新しい位置にワープし、元の位相値と新しい振幅値を使用して新しい複素数を計算します。長方形を極座標に変換し、長方形に戻すと、符号が失われます。複素数ベクトルの長さを変更するだけなので、入力符号を出力複素数に強制できます。
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();
}
}
2 番目の実装は、最初の実装よりもはるかに優れています。実際、私が使用したほとんどの DJ アプリケーションよりも優れたサウンドに聞こえますが、その理由はわかりません。私は何か間違ったことをしていますか?比較できる Java の他の実装は見つかりませんでした。彼らは通常、周波数スケール全体を新しいスケールの振幅と位相でワープするだけですか、それとも振幅を取得して別のスケールの元の位相に強制するだけですか?