Swift FFT - сложная проблема разделения

Я пытаюсь выполнить FFT в аудиофайле, чтобы найти частоту, используя фреймворк Accelerate. Я применил код (возможно, неверный) из этого вопроса: Спектрограмма от AVAudioPCMBuffer с использованием ускорения в Swift

Несмотря на то, что величины из "спектра" являются либо "0", "inf", либо "nan", а "реальные" и "воображаемые" компоненты комплексного разделения печатают аналогичные результаты; что это является причиной проблемы: "величина = sqrt (pow ( real, 2) + pow ( imag, 2) '. Исправьте меня, если i' неверно, но я думаю, что остальная часть кода в порядке.

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

Вот код, который я использую:

    // get audio file
    let fileURL:NSURL = NSBundle.mainBundle().URLForResource("foo", withExtension: "mp3")!
    let audioFile = try!  AVAudioFile(forReading: fileURL)
    let fileFormat = audioFile.processingFormat
    let frameCount = UInt32(audioFile.length)

    let buffer = AVAudioPCMBuffer(PCMFormat: fileFormat, frameCapacity: frameCount)
    let audioEngine = AVAudioEngine()
    let playerNode = AVAudioPlayerNode()
    audioMixerNode = audioEngine.mainMixerNode

    let bufferSize = Int(frameCount)
    let channels: NSArray = [Int(buffer.format.channelCount)]
    let channelCount = channels.count
    let floats1 = [Int(buffer.frameLength)]
    for var i=0; i<channelCount; ++i {
        channelSamples.append([])
        let firstSample = buffer.format.interleaved ? i : i*bufferSize
        for var j=firstSample; j<bufferSize; j+=buffer.stride*2 {
            channelSamples[i].append(DSPComplex(real: buffer.floatChannelData.memory[j], imag: buffer.floatChannelData.memory[j+buffer.stride]))
        }
    }

    // connect node
    audioEngine.attachNode(playerNode)
    audioEngine.connect(playerNode, to: audioMixerNode, format: playerNode.outputFormatForBus(0))

    // Set up the transform
    let log2n = UInt(round(log2(Double(bufferSize))))
    let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2))

    // Create the complex split value to hold the output of the transform
    // why doesn't this work?
    var realp = [Float](count: bufferSize/2, repeatedValue: 0)
    var imagp = [Float](count: bufferSize/2, repeatedValue: 0)
    var output = DSPSplitComplex(realp: &realp, imagp: &imagp)

    vDSP_ctoz(UnsafePointer(channelSamples), 2, &output, 1, UInt(bufferSize / 2))

    // Do the fast Fourier forward transform
    vDSP_fft_zrip(fftSetup, &output, 1, log2n, Int32(FFT_FORWARD))

    // Convert the complex output to magnitude
    var fft = [Float](count:Int(bufferSize / 2), repeatedValue:0.0)
    let bufferOver2: vDSP_Length = vDSP_Length(bufferSize / 2)
    vDSP_zvmags(&output, 1, &fft, 1, bufferOver2)

    var spectrum = [Float]()
    for var i=0; i<bufferSize/2; ++i {
        let imag = output.imagp[i]
        let real = output.realp[i]
        let magnitude = sqrt(pow(real,2)+pow(imag,2))
        spectrum.append(magnitude) }

    // Release the setup
    vDSP_destroy_fftsetup(fftSetup)

Ответ 1

Было несколько проблем с вашим кодом:

  1. вы не читали образцы аудио файлов
  2. channelSamples был упакован неправильно
  3. vDSP_fft_zrip читал за пределами массива. он ожидает 2 ^ log2n образцов
  4. vDSP_fft_zrip упакован, а ваши вычисления ожидают распаковки

Версия Swift 4 теперь с исправлением для пункта 3

let fileURL = Bundle.main.url(forResource: "foo", withExtension: "mp3")!
let audioFile = try!  AVAudioFile(forReading: fileURL as URL)
let frameCount = UInt32(audioFile.length)

let log2n = UInt(round(log2(Double(frameCount))))
let bufferSizePOT = Int(1 << log2n)

let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: AVAudioFrameCount(bufferSizePOT))!
try! audioFile.read(into: buffer, frameCount:frameCount)

// Not sure if AVAudioPCMBuffer zero initialises extra frames, so when in doubt...
let leftFrames = buffer.floatChannelData![0]
for i in Int(frameCount)..<Int(bufferSizePOT) {
    leftFrames[i] = 0
}

// Set up the transform
let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2))!

// create packed real input
var realp = [Float](repeating: 0, count: bufferSizePOT/2)
var imagp = [Float](repeating: 0, count: bufferSizePOT/2)
var output = DSPSplitComplex(realp: &realp, imagp: &imagp)

leftFrames.withMemoryRebound(to: DSPComplex.self, capacity: bufferSizePOT / 2) {
    vDSP_ctoz($0, 2, &output, 1, UInt(bufferSizePOT / 2))
}

// Do the fast Fourier forward transform, packed input to packed output
vDSP_fft_zrip(fftSetup, &output, 1, log2n, Int32(FFT_FORWARD))

// you can calculate magnitude squared here, with care
// as the first result is wrong! read up on packed formats
var fft = [Float](repeating:0.0, count:Int(bufferSizePOT / 2))
vDSP_zvmags(&output, 1, &fft, 1, vDSP_Length(bufferSizePOT / 2))

// Release the setup
vDSP_destroy_fftsetup(fftSetup)