Как реализовать эффект Pitch в Java? (FFT, IFFT, амплитуда, фаза)

Я использую математическую библиотеку apache commons для преобразования FFt и IFFT в моих буферах аудиосэмплов. Выход из БПФ дает мне массив комплексных чисел. Частоты отражаются посередине. При размере буфера 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 для сравнения. Обычно они просто деформируют всю частотную шкалу с амплитудой и фазой в новом масштабе, или они просто берут амплитуду и вводят ее в исходную фазу в другом масштабе?

1 ответ

Решение

Ваш второй алгоритм аналогичен методу фазового вокодера для изменения высоты тона. Сообщается, что во многих библиотеках обработки аудио используются вариации метода фазового вокодера, но обычно только при наличии достаточной вычислительной мощности.

Другие вопросы по тегам