2

オーディオ サンプル バッファで 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 の他の実装は見つかりませんでした。彼らは通常、周波数スケール全体を新しいスケールの振幅と位相でワープするだけですか、それとも振幅を取得して別のスケールの元の位相に強制するだけですか?

4

1 に答える 1

2

2 番目のアルゴリズムは、時間ピッチ変更のフェーズ ボコーダー メソッドに似ています。多くのオーディオ処理ライブラリは、位相ボコーダー技術のバリエーションを使用することが報告されていますが、通常は十分な処理能力が利用できる場合に限られます。

于 2016-08-04T19:35:32.217 に答える