TL; ДР: Если вам нужна рабочая реализация, чтобы скопировать прошлое , то есть суть .
Что такое БПФ?
Быстрое преобразование Фурье - это алгоритм, который принимает сигнал в временная область - совокупность измерений, проводимых с регулярным, обычно небольшим интервалом времени, - и превращающая ее в сигнал, выраженный в фазовой области (совокупность частот). Возможность express сигнала в течение времени, потерянного преобразованием (преобразование является обратимым, что означает, что при вычислении FFT не теряется информация, и вы можете применить IFFT для получения исходного сигнала обратно), но мы получаем возможность различайте guish между частотами, содержащимися в сигнале. Это обычно используется для отображения спектрограмм музыки, которую вы слушаете * на различных видеооборудованиях и видео на YouTube.
БПФ работает с комплексными числами . Если вы не знаете, что это такое, давайте представим, что это комбинация радиуса и угла. Существует одно комплексное число на точку на 2D-плоскости. Вещественные числа (ваши обычные числа с плавающей точкой) можно рассматривать как позицию на линии (отрицательное слева, положительное справа).
Nb: FFT (FFT (FFT (FFT (X)))) = X (с точностью до константы в зависимости от вашей реализации БПФ).
Как вычислить БПФ реального сигнала.
Обычно вы хотите вычислить БПФ небольшого окна аудиосигнала. В качестве примера мы возьмем небольшое окно с образцами 1024. Вы также предпочли бы использовать степень двойки, в противном случае все становится немного сложнее.
var signal: [Float] // Array of length 1024
Во-первых, вам нужно инициализировать некоторые константы для вычислений.
// The length of the input
length = vDSP_Length(signal.count)
// The power of two of two times the length of the input.
// Do not forget this factor 2.
log2n = vDSP_Length(ceil(log2(Float(length * 2))))
// Create the instance of the FFT class which allow computing FFT of complex vector with length
// up to `length`.
fftSetup = vDSP.FFT(log2n: log2n, radix: .radix2, ofType: DSPSplitComplex.self)!
Следуя документации Apple, нам сначала нужно создать сложный массив, который будет нашим вводом. Учебник не вводит в заблуждение. Обычно вы хотите скопировать ваш сигнал как действительную часть ввода и оставьте комплексную часть нулевой.
// Input / Output arrays
var forwardInputReal = [Float](signal) // Copy the signal here
var forwardInputImag = [Float](repeating: 0, count: Int(length))
var forwardOutputReal = [Float](repeating: 0, count: Int(length))
var forwardOutputImag = [Float](repeating: 0, count: Int(length))
Будьте осторожны, функция FFT не позволяет использовать один и тот же splitComplex в качестве входа и выхода одновременно. аварии, это может быть причиной. Вот почему мы определяем как вход, так и выход.
Теперь мы должны быть осторожны и «заблокировать» указатель на эти четыре массива, как показано в примере документации. Если вы просто используете &forwardInputReal
в качестве аргумента вашего DSPSplitComplex
, указатель может стать недействительным в следующей строке, и вы, скорее всего, увидите sporadi c cra sh вашего приложения.
forwardInputReal.withUnsafeMutableBufferPointer { forwardInputRealPtr in
forwardInputImag.withUnsafeMutableBufferPointer { forwardInputImagPtr in
forwardOutputReal.withUnsafeMutableBufferPointer { forwardOutputRealPtr in
fforwardOutputImag.withUnsafeMutableBufferPointer { forwardOutputImagPtr in
// Input
let forwardInput = DSPSplitComplex(realp: forwardInputRealPtr.baseAddress!, imagp: forwardInputImagPtr.baseAddress!)
// Output
var forwardOutput = DSPSplitComplex(realp: forwardOutputRealPtr.baseAddress!, imagp: forwardOutputImagPtr.baseAddress!)
// FFT call goes here
}
}
}
}
Теперь финальная строка: вызов вашего БПФ:
fftSetup.forward(input: forwardInput, output: &forwardOutput)
Результат вашего БПФ теперь доступен в forwardOutputReal
и forwardOutputImag
.
Если вам нужна только амплитуда каждой частоты, и вы не заботитесь о действительной и мнимой части, вы можете объявить рядом с входом и вывести дополнительный массив:
var magnitudes = [Float](repeating: 0, count: Int(length))
add сразу после того, как ваш fft вычислит амплитуду каждого "бина" с:
vDSP.absolute(forwardOutput, result: &magnitudes)