swift 3 FFT得到声音m4a的频率

时间:2016-12-04 16:04:12

标签: swift audio buffer fft frequency

我是编程ios的初学者。 我必须为我的学校做一个项目。目标是使用麦克风进行录制,然后应用高通滤波器并将其保存在m4a文件中。

在这个网站和其他许多网站上,我发现了许多相同的代码但是随着swift 3的到来,代码不再有用。

我首先使用AVAudioPlayer录制并保存麦克风的内容。

然后我在mpc缓冲区中读取我的文件。

我在float数组中从缓冲区中检索数据。

最后,我按照我发现的例子应用了FFT。

如果我显示缓冲区的数据(来自缓冲区的浮动表),它们包含一些内容。

如果我显示我的VDSP矢量,它包含数据。

但是当我应用FFT时,包含卷轴和虚数的VDSP输出结果会返回“nan”值。

这里我不理解FFT的功能,我不明白重新组合“输出”是否包含频率,或者它是否只是我的VDSP中包含它们的参数之一。 (真实的或想象的):

然后我考虑对这些结果应用滤波器,然后将我的值重新放回到逆FFT中,以便通过修改重建m4a文件。

如果您可以向我解释我的方法是假的还是我的代码

// recupere le lien du fichier audio a analysé
        let url = getDocumentsDirectory().appendingPathComponent("recording.m4a")
        // lancé l'audio dans le core AVaudioFile
        let audioFile = try!  AVAudioFile(forReading: url)

        // nombre de frame dans l'audio
        let frameCount = UInt32(audioFile.length)

        print("frame count\(frameCount)")
        //remplis un buffer avec les information du son et le nombre de framme
        let buffer = AVAudioPCMBuffer(pcmFormat: audioFile.processingFormat, frameCapacity: frameCount)
        do {
            //lecture de l'audio dans le buffer
            try audioFile.read(into: buffer, frameCount:frameCount)
            print("lecture ok")
        } catch {
            //lecture échouer

        }

        print(buffer.floatChannelData?.pointee ?? "aucune valeur float")

        // printer le buffer de byte de l'audio
        print("\n buffer: \n");
        for k in 1...frameCount
        {
            print("value buffer \(buffer.floatChannelData?.pointee[Int(k)])");
        }


        // définit un fonction log2n
        let log2n = UInt(round(log2(Double(frameCount))))

        // définit la taille de buffer final potentiel
        let bufferSizePOT = Int(1 << log2n)

        //crée une structure FFT
        //Si zéro est renvoyé, la routine n'a pas réussi à allouer de stockage
        let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2))

        //print fft
        print("valeur du fftSetup \(fftSetup)")


        // create packed real input
        // séparation des buffer en nombre réel et imaginaire :

        var realp = [Float](repeating: 0.0, count: bufferSizePOT/2)
        var imagp = [Float](repeating: 0.0, count: bufferSizePOT/2)

        /*
        print("\n real and image: \n");
         for k in 0..<realp.count
         {
         print("value real \(realp[k]) et value imaginaire \(imagp[k])");
         }
        */

        // construit un vecteur double contenant les real et les imaginaire
        var output = DSPSplitComplex(realp: &realp, imagp: &imagp)

        buffer.floatChannelData?.withMemoryRebound(to: DSPComplex.self, capacity: bufferSizePOT/2) {
            /*
             Copie le contenu d'un vecteur complexe intercalé C vers un vecteur complexe divisé Z; Précision unique.

             void vDSP_ctoz(const DSPComplex *__C, vDSP_Stride __IC, const DSPSplitComplex *__Z, vDSP_Stride __IZ, vDSP_Length __N);
             Paramètres
             __C
             Vecteur d'entrée complexe entrelacé à simple précision.
             __IC
             Stride pour C; Doit être un nombre pair.
             __Z
             Vecteur de sortie complexe à division simple.
             za
             Stride pour Z.
             __N
             Le nombre d'éléments à traiter.

             */
            dspComplexStream in vDSP_ctoz(dspComplexStream, 2, &output, 1, UInt(bufferSizePOT / 2))
        }

        /*
         calcul la série de fourier discrette du domaine temporel ver le domaine fréquentielle
         paramètre :

         func vDSP_fft_zrip(_ __Setup: 
         - --FFTSetup:  l'objet FFTsetup
         _ __C:         pointeur sur le vecteur complex de sortie
         _ __IC:        pas entre les elements de --C, (a 1 pour des meilleures performance)
         _ __Log2N:     Il base 2 exposant du nombre d'éléments à traiter. Par exemple, pour traiter 1024 éléments,
                        spécifiez 10 pour le paramètre Log2N.
         _ __Direction: FFTDirection : donne la direction de la discretisations. 
                        time domain to the frequency domain  = (forward).
                        frequency domain to the time domain (inverse).
         )*/
        vDSP_fft_zrip(fftSetup!, &output, 1, log2n, Int32(FFTDirection(FFT_FORWARD)))


        print("\nSpectrum:\n");
        for i in 0..<realp.count
        {
            print("value de i \(i), réel : \(output.realp[i]), imaginaire : \(imagp[i])");
        }

        var fft = [Float](repeating:0.0, count:Int(bufferSizePOT / 2))
        let bufferOver2: vDSP_Length = vDSP_Length(bufferSizePOT / 2)

        vDSP_zvmags(&output, 1, &fft, 1, bufferOver2)
        for i in 0..<bufferSizePOT/2
        {
            print("value de buff2 \(fft[i])");
        }
        // termine le processus FFT
        vDSP_destroy_fftsetup(fftSetup)

编辑:juste播放带过滤器低通的歌曲,不能正常工作

 engine = AVAudioEngine()
        player = AVAudioPlayerNode()
        player.volume = 1.0

        let path = Bundle.main.path(forResource: "audio10to1000", ofType: "wav")!
        let url = NSURL.fileURL(withPath: path)

        let file = try? AVAudioFile(forReading: url)

        var mainMixer = AVAudioMixerNode()

        mainMixer = engine.mainMixerNode

        engine.attach(player)

        EQNode = AVAudioUnitEQ(numberOfBands: 1)




        var filterParams = EQNode.bands[0] as AVAudioUnitEQFilterParameters

        filterParams.filterType = .lowPass
        filterParams.frequency = 500.0
        filterParams.bypass = false
        engine.attach(EQNode)


        engine.connect(player, to: EQNode, format: file?.processingFormat)
        engine.connect(EQNode, to: mainMixer, format: file?.processingFormat)


       // engine.connect(player, to: mainMixer, format: file?.processingFormat)

        player.scheduleFile(file!, at: nil, completionHandler: nil)

        engine.prepare()
        do {
            try engine.start()
        } catch _ {
            print("******************* erreur *************")
        }

        player.play()

1 个答案:

答案 0 :(得分:1)

您的代码正常运行。您的设置不符合您的需求。

  • 设置filterParams.gain = -96.0(最小可能值)

  • 低通滤波器没有bandwidth,删除它。

对于更激进的结果,首先将截止频率设置为100 HZ:

  • filterParams.frequency = 100.0

您对低通滤波器结果的预期(截止频率高于100%)不符合滤波器的实际情况。根据实现(使用的算法和顺序),过滤器或多或少会快速切断。

请参阅此典型过滤器响应from Wikipedia