Filename: fft.hs Created by: Daniel Winograd-Cort Created on: unknown Last Modified by: Daniel Winograd-Cort Last Modified on: 16-Dec-2015 by Donya Quick This module requires the array and pure-fft packages. > {-# LANGUAGE Arrows #-} > module HSoM.Examples.FFT where > import FRP.UISF > import Control.Arrow.Operations > import Numeric.FFT (fft) > import Data.Complex > import Data.Map (Map) > import qualified Data.Map as Map Alternative for working with Math.FFT instead of Numeric.FFT import qualified Math.FFT as FFT import Data.Array.IArray import Data.Array.CArray myFFT n lst = elems $ (FFT.dft) (listArray (0, n-1) lst) -------------------------------------- -- Fast Fourier Transform -------------------------------------- Returns n samples of type b from the input stream at a time, updating after k samples. This function is good for chunking data and is a critical component to fftA > quantize :: ArrowCircuit a => Int -> Int -> a b (SEvent [b]) > quantize n k = proc d -> do > rec (ds,c) <- delay ([],0) -< (take n (d:ds), c+1) > returnA -< if c >= n && c `mod` k == 0 then Just ds else Nothing Converts the vector result of a dft into a map from frequency to magnitude. One common use is: fftA >>> arr (fmap $ presentFFT clockRate) > presentFFT :: Double -> [Double] -> Map Double Double > presentFFT clockRate a = Map.fromList $ zipWith (curry mkAssoc) [0..] a where > mkAssoc (i,c) = (freq, c) where > samplesPerPeriod = fromIntegral (length a) > freq = i * (clockRate / samplesPerPeriod) Given a quantization frequency (the number of samples between each successive FFT calculation) and a fundamental period, this will decompose the input signal into its constituent frequencies. NOTE: The fundamental period must be a power of two! > fftA :: ArrowCircuit a => Int -> Int -> a Double (SEvent [Double]) > fftA qf fp = proc d -> do > carray <- quantize fp qf -< d :+ 0 > returnA -< fmap (map magnitude . take (fp `div` 2) . fft) carray