synthesizer-core-0.9: test/Test/Sound/Synthesizer/Generic/Fourier.hs
{-# LANGUAGE NoImplicitPrelude #-}
module Test.Sound.Synthesizer.Generic.Fourier (tests) where
import qualified Synthesizer.Generic.Fourier as Fourier
import qualified Synthesizer.Generic.Cyclic as Cyclic
import qualified Synthesizer.Generic.Filter.NonRecursive as FiltNRG
import qualified Synthesizer.Generic.Analysis as AnaG
import qualified Synthesizer.Generic.Signal as SigG
import qualified Synthesizer.Generic.Cut as CutG
import qualified Synthesizer.Storable.Signal as SigSt
import qualified Synthesizer.State.Signal as SigS
import qualified Test.QuickCheck as QC
import Test.QuickCheck (Testable, Arbitrary, arbitrary, property)
import Test.Utility (approxEqualAbs, approxEqualComplexAbs)
import qualified Number.Complex as Complex
import qualified Algebra.Ring as Ring
import qualified Algebra.Additive as Additive
import Control.Monad (liftM2, )
import NumericPrelude.Numeric
import NumericPrelude.Base
import Prelude ()
tolerance :: Double
tolerance = 1e-10
normalize ::
SigSt.T (Complex.T Double) -> SigSt.T (Complex.T Double)
normalize xs =
FiltNRG.amplifyVector
(recip $ max (0.1::Double) $ AnaG.volumeVectorMaximum xs) xs
newtype Normed = Normed (SigSt.T (Complex.T Double))
deriving (Show)
instance Arbitrary Normed where
arbitrary = fmap (Normed . normalize) arbitrary
data Normed2 =
Normed2
(SigSt.T (Complex.T Double))
(SigSt.T (Complex.T Double))
deriving (Show)
instance Arbitrary Normed2 where
arbitrary =
liftM2
(\x y ->
let len = min (CutG.length x) (CutG.length y)
in Normed2
(normalize $ CutG.take len x)
(normalize $ CutG.take len y))
arbitrary
arbitrary
-- could be moved to NumericPrelude
class Complex a where
conjugate :: a -> a
instance (Additive.C a) => Complex (Complex.T a) where
conjugate = Complex.conjugate
scalarProduct ::
(SigG.Consume sig y, Ring.C y, Complex y) =>
sig y -> sig y -> y
scalarProduct xs ys =
SigS.sum $
SigS.zipWith (*)
(SigG.toState xs)
(SigS.map conjugate $ SigG.toState ys)
(=~=) ::
SigSt.T (Complex.T Double) ->
SigSt.T (Complex.T Double) ->
Bool
(=~=) xs ys =
SigG.length xs == SigG.length ys &&
(SigG.foldR (&&) True $
SigG.zipWith (approxEqualComplexAbs tolerance) xs ys)
simple ::
(Testable t) =>
(SigSt.T (Complex.T Double) -> t) -> QC.Property
simple = property
tests :: [(String, QC.Property)]
tests =
("fourier inverse",
property $ \(Normed x) ->
x =~=
(FiltNRG.amplify (recip $ fromIntegral $ SigG.length x) $
Fourier.transformBackward $ Fourier.transformForward x)) :
("double fourier = reverse",
property $ \(Normed x) ->
x =~=
(Cyclic.reverse $
FiltNRG.amplify (recip $ fromIntegral $ SigG.length x) $
Fourier.transformForward $
Fourier.transformForward x)) :
("fourier of reverse",
property $ \(Normed x) ->
Cyclic.reverse (Fourier.transformForward x) =~=
Fourier.transformForward (Cyclic.reverse x)) :
("fourier of conjugate",
property $ \(Normed x) ->
(SigG.map Complex.conjugate $ Fourier.transformForward x)
=~=
(Fourier.transformForward $
SigG.map Complex.conjugate $ Cyclic.reverse x)) :
("additivity",
property $ \(Normed2 x y) ->
SigG.mix (Fourier.transformForward x) (Fourier.transformForward y)
=~=
Fourier.transformForward (SigG.mix x y)) :
("isometry",
simple $ \xs x0 ->
let x = normalize (SigG.cons x0 xs)
in approxEqualAbs tolerance
(AnaG.volumeVectorEuclideanSqr $ Fourier.transformForward x)
(fromIntegral (SigG.length x) *
AnaG.volumeVectorEuclideanSqr x)) :
("unitarity",
property $ \(Normed2 x y) ->
approxEqualComplexAbs tolerance
(scalarProduct
(Fourier.transformForward x) (Fourier.transformForward y))
(fromIntegral (SigG.length x) * scalarProduct x y)) :
("convolution",
property $ \(Normed2 x y) ->
SigG.zipWith (*)
(Fourier.transformForward x)
(Fourier.transformForward y)
=~=
Fourier.transformForward (Cyclic.convolve x y)) :
("convolution cyclic",
property $ \(Normed2 x y) ->
Fourier.convolveCyclic x y
=~=
Cyclic.convolve x y) :
("convolution long",
property $ \(Normed x) (Normed y) ->
FiltNRG.karatsubaFinite (*) x y
=~=
Fourier.convolveWithWindow (Fourier.window x) y) :
[]