Извлечение точных частот из ячеек БПФ с использованием изменения фазы между кадрами

Я просматривал эту фантастическую статью: http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/

Несмотря на то, что это фантастика, это чрезвычайно сложно и тяжело. Этот материал действительно растягивает меня.

Я извлек математику из модуля кода Стефана, который вычисляет точную частоту для данного бина. Но я не понимаю последнего расчета. Может кто-нибудь объяснить мне математическую конструкцию в конце?

Прежде чем копаться в коде, позвольте мне установить сцену:

  • Допустим, мы установили fftFrameSize = 1024, поэтому мы имеем дело с 512+1 бинами.

  • Например, идеальная частота Bin[1] соответствует одной волне в кадре. При частоте дискретизации 40 кГц tOneFrame = 1024/40K секунд = 1/40 с, поэтому Bin[1] в идеале будет собирать сигнал 40 Гц.

  • Установив osamp (overSample) = 4, мы продвигаемся по нашему входному сигналу шагами по 256. Таким образом, первый анализ проверяет байты с нуля до 1023, затем с 256 по 1279 и т. д. Обратите внимание, что каждое число с плавающей запятой обрабатывается 4 раза.

...

void calcBins( 
              long fftFrameSize, 
              long osamp, 
              float sampleRate, 
              float * floats, 
              BIN * bins
              )
{
    /* initialize our static arrays */
    static float gFFTworksp[2*MAX_FRAME_LENGTH];
    static float gLastPhase[MAX_FRAME_LENGTH/2+1];

    static long gInit = 0;
    if (! gInit) 
    {
        memset(gFFTworksp, 0, 2*MAX_FRAME_LENGTH*sizeof(float));
        memset(gLastPhase, 0, (MAX_FRAME_LENGTH/2+1)*sizeof(float));
        gInit = 1;
    }

    /* do windowing and re,im interleave */
    for (long k = 0; k < fftFrameSize; k++) 
    {
        double window = -.5*cos(2.*M_PI*(double)k/(double)fftFrameSize)+.5;
        gFFTworksp[2*k] = floats[k] * window;
        printf("sinValue: %f", gFFTworksp[2*k]);
        gFFTworksp[2*k+1] = 0.;
    }

    /* do transform */
    smbFft(gFFTworksp, fftFrameSize, -1);

    printf("\n");

    /* this is the analysis step */
    for (long k = 0; k <= fftFrameSize/2; k++) 
    {
        /* de-interlace FFT buffer */
        double real = gFFTworksp[2*k];
        double imag = gFFTworksp[2*k+1];

        /* compute magnitude and phase */
        double magn = 2.*sqrt(real*real + imag*imag);
        double phase = atan2(imag,real);

        /* compute phase difference */
        double phaseDiff = phase - gLastPhase[k];
        gLastPhase[k] = phase;

        /* subtract expected phase difference */
        double binPhaseOffset = M_TWOPI * (double)k / (double)osamp;
        double deltaPhase = phaseDiff - binPhaseOffset;

        /* map delta phase into [-Pi, Pi) interval */
        // better, but obfuscatory...
        //    deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);

        while (deltaPhase >= M_PI)
            deltaPhase -= M_TWOPI;
        while (deltaPhase < -M_PI)
            deltaPhase += M_TWOPI;

(РЕДАКТИРОВАТЬ:) Теперь то, чего я не понимаю:

        // Get deviation from bin frequency from the +/- Pi interval 
        // Compute the k-th partials' true frequency    

        // Start with bin's ideal frequency
        double bin0Freq = (double)sampleRate / (double)fftFrameSize;
        bins[k].idealFreq = (double)k * bin0Freq;

        // Add deltaFreq
        double sampleTime = 1. / (double)sampleRate;
        double samplesInStep = (double)fftFrameSize / (double)osamp;
        double stepTime = sampleTime * samplesInStep;
        double deltaTime = stepTime;        

        // Definition of frequency is rate of change of phase, i.e. f = dϕ/dt
        // double deltaPhaseUnit = deltaPhase / M_TWOPI; // range [-.5, .5)
        double freqAdjust = (1. / M_TWOPI) * deltaPhase / deltaTime; 

        // Actual freq <-- WHY ???
        bins[k].freq = bins[k].idealFreq + freqAdjust;
    }
}

Я просто не могу ясно его видеть, хотя кажется, что он смотрит прямо в лицо. Может ли кто-нибудь объяснить этот процесс с нуля, шаг за шагом?


person P i    schedule 08.01.2011    source источник
comment
Как можно получить BIN * bins что это означает?   -  person Mord Fustang    schedule 21.03.2015


Ответы (6)


Основной принцип очень прост. Если данный компонент точно соответствует частоте бина, то его фаза не будет меняться от одного FT к другому. Однако, если частота не соответствует точно частоте бина, тогда будет фазовый сдвиг между последовательными FT. Дельта частоты равна:

delta_freq = delta_phase / delta_time

и тогда уточненная оценка частоты компонента будет:

freq_est = bin_freq + delta_freq
person Paul R    schedule 08.01.2011
comment
Извините за глупость, но я до сих пор не понимаю, почему это правда. Я все еще чувствую себя очень необоснованным, используя эту математику. - person P i; 08.01.2011
comment
Если 2 БПФ смещены на величину, отличную от одного периода синусоидальной волны, то произойдет изменение фазы, даже если частота синусоидальной волны находится в центре бина. - person hotpaw2; 08.01.2011
comment
Также полезно знать, что одним из определений частоты является скорость изменения фазы, то есть f = dϕ/dt. - person Paul R; 08.01.2011
comment
Рискну предположить, что кто-то завидует вашему l33tDSPsk1llz :p ну, это не я. Я чрезвычайно благодарен и вам, и HotPaw за свежий взгляд. теперь я действительно могу это понять - наконец-то!!! - person P i; 08.01.2011
comment
@Ohmu: рад слышать, что вы делаете успехи — я рекомендую прочитать хорошую вводную книгу по DSP, если вы собираетесь заниматься чем-то еще — книгу Ричарда Лайонса Understanding Digital Signal Processing , очень хорош и намного практичнее, чем большинство. - person Paul R; 08.01.2011
comment
На самом деле dϕ/dt = -2π.f (я только что просмотрел вывод) - person P i; 06.02.2011
comment
@Ohmu - это зависит от того, определяете ли вы частоту в радианах в секунду или в герцах, но вы, вероятно, правы насчет знака -. - person Paul R; 06.02.2011

Я сам реализовал этот алгоритм для Performous. Когда вы берете другое БПФ со смещением по времени, вы ожидаете, что фаза изменится в соответствии со смещением, т. е. два БПФ, взятые с интервалом 256 отсчетов, должны иметь разность фаз 256 отсчетов для всех частот, присутствующих в сигнале (это предполагает, что сами сигналы стабильны, что является хорошим предположением для коротких периодов, таких как 256 выборок).

Теперь фактические значения фазы, которые вы получаете из БПФ, представлены не в выборках, а в фазовом угле, поэтому они будут разными в зависимости от частоты. В следующем коде значение PhaseStep является коэффициентом преобразования, необходимым для каждого бина, т. е. для частоты, соответствующей бину x, фазовый сдвиг будет равен x * PhaseStep. Для центральных частот бина x будет целым числом (номер бина), но для фактически обнаруженных частот это может быть любое действительное число.

const double freqPerBin = SAMPLE_RATE / FFT_N;
const double phaseStep = 2.0 * M_PI * FFT_STEP / FFT_N;

Коррекция работает, предполагая, что сигнал в бине имеет центральную частоту бина, а затем вычисляя для этого ожидаемый фазовый сдвиг. Это ожидаемое смещение вычитается из фактического сдвига, оставляя ошибку. Берется остаток (по модулю 2 пи) (диапазон от -пи до пи), и окончательная частота рассчитывается с центром бина + поправкой.

// process phase difference
double delta = phase - m_fftLastPhase[k];
m_fftLastPhase[k] = phase;
delta -= k * phaseStep;  // subtract expected phase difference
delta = remainder(delta, 2.0 * M_PI);  // map delta phase into +/- M_PI interval
delta /= phaseStep;  // calculate diff from bin center frequency
double freq = (k + delta) * freqPerBin;  // calculate the true frequency

Обратите внимание, что многие соседние бины часто корректируются до одной и той же частоты, потому что дельта-коррекция может составлять до 0,5 * бинов FFT_N / FFT_STEP в любом случае, поэтому чем меньший FFT_STEP вы используете, тем дальше будут возможны коррекции (но это увеличивает вычислительную мощность). необходимости, а также неточности из-за неточностей).

Надеюсь, это поможет :)

person Tronic    schedule 06.02.2011
comment
Теперь у меня есть несколько обоснований «стиля эссе», на которые стоит обратить внимание. но я недостаточно умен, чтобы самостоятельно сформулировать математику из этих объяснений. Я после некоторого объяснения, которое генерирует математику построчно. Математическое доказательство. - person P i; 06.02.2011
comment
Может быть, это поможет? sengpielaudio.com/calculator-timedelayphase.htm (время задержки в миллисекундах но я полагаю, вы можете преобразовать 256 сэмплов в нужное количество времени) - person Tronic; 06.02.2011

Это метод оценки частоты, используемый в методах фазового вокодера.

Если вы посмотрите на одну точку синусоидальной волны (фиксированной частоты и фиксированной амплитуды) во времени, фаза будет увеличиваться со временем на величину, пропорциональную частоте. Или вы можете сделать обратное: если вы измерите, насколько фаза синусоиды изменяется в любую единицу времени, вы можете вычислить частоту этой синусоиды.

Фазовый вокодер использует два БПФ для оценки фазы относительно двух окон БПФ, а смещение двух БПФ представляет собой расстояние между двумя измерениями фазы во времени. Отсюда у вас есть оценка частоты для этого бина БПФ (бинар БПФ, грубо говоря, является фильтром для выделения синусоидальной составляющей или другого достаточно узкополосного сигнала, который помещается в этот бин).

Чтобы этот метод работал, спектр вблизи используемого бина БПФ должен быть достаточно стационарным, т.е. не меняется частота и т. д. Это предположение требуется для фазового вокодера.

person hotpaw2    schedule 08.01.2011

Наконец я понял это; действительно мне пришлось вывести его с нуля. Я знал, что будет какой-то простой способ вывести его, моя (обычная) ошибка заключалась в попытке следовать логике других людей, а не использовать собственный здравый смысл.

Эта головоломка требует два ключа, чтобы разблокировать ее.

...

for (int k = 0; k <= fftFrameSize/2; k++) 
{
    // compute magnitude and phase 
    bins[k].mag = 2.*sqrt(fftBins[k].real*fftBins[k].real + fftBins[k].imag*fftBins[k].imag);
    bins[k].phase = atan2(fftBins[k].imag, fftBins[k].real);

    // Compute phase difference Δϕ fo bin[k]
    double deltaPhase;
    {
        double measuredPhaseDiff = bins[k].phase - gLastPhase[k];
        gLastPhase[k] = bins[k].phase;

        // Subtract expected phase difference <-- FIRST KEY
        // Think of a single wave in a 1024 float frame, with osamp = 4
        //   if the first sample catches it at phase = 0, the next will 
        //   catch it at pi/2 ie 1/4 * 2pi
        double binPhaseExpectedDiscrepancy = M_TWOPI * (double)k / (double)osamp;
        deltaPhase = measuredPhaseDiff - binPhaseExpectedDiscrepancy;

        // Wrap delta phase into [-Pi, Pi) interval 
        deltaPhase -= M_TWOPI * floor(deltaPhase / M_TWOPI + .5);
    }

    // say sampleRate = 40K samps/sec, fftFrameSize = 1024 samps in FFT giving bin[0] thru bin[512]
    // then bin[1] holds one whole wave in the frame, ie 44 waves in 1s ie 44Hz ie sampleRate / fftFrameSize
    double bin0Freq = (double)sampleRate / (double)fftFrameSize;
    bins[k].idealFreq = (double)k * bin0Freq;

    // Consider Δϕ for bin[k] between hops.
    // write as 2π / m.
    // so after m hops, Δϕ = 2π, ie 1 extra cycle has occurred   <-- SECOND KEY
    double m = M_TWOPI / deltaPhase;

    // so, m hops should have bin[k].idealFreq * t_mHops cycles.  plus this extra 1.
    // 
    // bin[k].idealFreq * t_mHops + 1 cycles in t_mHops seconds 
    //   => bins[k].actualFreq = bin[k].idealFreq + 1 / t_mHops
    double tFrame = fftFrameSize / sampleRate;
    double tHop = tFrame / osamp;
    double t_mHops = m * tHop;

    bins[k].freq = bins[k].idealFreq + 1. / t_mHops;
}
person P i    schedule 08.02.2011
comment
РЕДАКТИРОВАТЬ: см. мой ответ на math.stackexchange.com/questions/9416/, чтобы понять ротацию корзин - person P i; 06.05.2014

Может быть, это поможет. Думайте о ячейках БПФ как о маленьких часах или роторах, каждый из которых вращается с частотой ячейки. Для стабильного сигнала (теоретическое) следующее положение ротора можно предсказать, используя математику в бите, который вы не получаете. По отношению к этому «должно быть» (идеальному) положению вы можете вычислить несколько полезных вещей: (1) разницу с фазой в ячейке соседнего кадра, которая используется фазовым вокодером для лучшего оценка частоты бина или (2) в более общем случае отклонение фазы, которое является положительным индикатором начала ноты или какого-либо другого события в аудио.

person Bill    schedule 24.04.2012

Частоты сигналов, попадающие точно на частоту элемента разрешения, опережают фазу элемента разрешения на целое число, кратное 2π. Поскольку фазы бинов, соответствующие частотам бинов, кратны 2π из-за периодического характера БПФ, в этом случае фазового изменения нет. Упомянутая вами статья также объясняет это.

person tahome    schedule 06.02.2011
comment
Это было бы верно, если бы шаг БПФ был таким же, как размер БПФ. Однако здесь шаги делаются меньше (коэффициент osamp), и тогда фаза уже не остается неизменной даже для центральных частот. Например. рассмотрим шаг БПФ только одной выборки. Для более низких частот фазового сдвига практически не будет, в то время как для очень высоких частот может быть разность фаз вплоть до PI. - person Tronic; 06.02.2011
comment
Я ответил на свой вопрос. Но если я дам награду за свой ответ, она будет потеряна. Я собирался отдать его Тронику из-за его потрясающего проекта с открытым исходным кодом (Performous), но у него масса баллов! Так что... наслаждайтесь ;) - person P i; 08.02.2011