swift 3 FFT获取声音m4a的频率

Posted

技术标签:

【中文标题】swift 3 FFT获取声音m4a的频率【英文标题】:swift 3 FFT get frequency of sound m4a 【发布时间】:2016-12-04 16:04:12 【问题描述】:

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

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

我首先使用 AVAudioPlayer 录制并保存从我的麦克风发出的声音。

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

我从浮点数组的缓冲区中检索数据。

最后,我像我找到的示例一样应用 FFT。

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

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

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

在这里,我不了解 FFT 的功能,也不了解 resutat“输出”是否包含频率,或者它是否只是包含频率的 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)

编辑:用过滤器低通播放歌曲并且不工作

 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()

【问题讨论】:

您不需要 FFT 来应用高切滤波器。谷歌AVAudioUnitEQ。如果您想自己实现一个,请先选择 FIR 或 IIR 解决方案:en.wikipedia.org/wiki/Low-pass_filter 玩得开心! hiii 感谢您的帮助。我已经努力使用 AVAudioUnitEQ。它在mixernode中使用的所有效果(示例spitch)都可以正常工作。但 Eq 节点不起作用。这就是我的代码:3 天就可以了,没有任何工作...... 没什么可改变的......再次感谢您的帮助只是为了确保我理解。当我使用 500 Hz 低通滤波器时。如果我的音频是一首以 10hz 开始的生成歌曲,然后在 10 秒内以 1000hz 结束,那么在一半的时间里我应该什么都听不到了?当我改变 EQ 的 gainGlobal 时,这项工作但 filterParams 中的增益没有任何改变.. 如果你发一个示例项目的链接,我去看看。 drive.google.com/file/d/0B7qN8KEpAsYeQUZtOG9ab1R6Sjg/… 谢谢@shallowthought 【参考方案1】:

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

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

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

为了更彻底的结果,首先将截止频率设置为 100 HZ:

filterParams.frequency = 100.0

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

查看这个典型的过滤器响应from Wikipedia:

【讨论】:

您好,抱歉,我还需要您的帮助。我有音频的大小标签。我正在应用我需要的所有过滤器。但我不知道如何在我的音频中花时间,当噪音开始和结束时。你有什么想法吗? 我如何理解您想要做的是确定幅度(音量值)何时超过阈值。我做对了吗?当你真正开始听到/停止听到声音时。 好的,基本上,我需要帮助的是在样本中拾取某种声音。我要查找的信息是样本中声音开始和结束的时间(以时间单位(ms、s 或其他)为单位)。我的样本是一个人服用呼吸系统药物的录音。我减弱了环境噪音,这样我就可以得到那个人正在服药的部分。我想要那个人开始服药和声音结束的时间戳 这里跑题了。 Lets chat.

以上是关于swift 3 FFT获取声音m4a的频率的主要内容,如果未能解决你的问题,请参考以下文章

声音 FFT 频率

FFT算法得到错误的声音频率值

AurioTouch2声音频率

声音分析 FFT

如果有任何方法可以获得声音的频率。

Android:通过fft获取更精确的频率