packages feed

synthesizer-0.2: src/Test/Sound/Synthesizer/Generic/ToneModulation.hs

module Test.Sound.Synthesizer.Generic.ToneModulation (tests) where

import Test.Sound.Synthesizer.Basic.ToneModulation (
   minLength,
   minLengthMargin,
--   shapeLimits,
--   testRationalLineIp,
   testRationalIp,
   )

import Test.Sound.Synthesizer.Plain.ToneModulation (
   InfiniteList,
   listFromInfinite,
   )

import qualified Synthesizer.Causal.ToneModulation as ToneModC
import qualified Synthesizer.Generic.Wave as WaveG

import qualified Synthesizer.Plain.Signal         as Sig
import qualified Synthesizer.Plain.Oscillator     as Osci
import qualified Synthesizer.Plain.Interpolation  as Interpolation
import qualified Synthesizer.Plain.ToneModulation as ToneModL
import qualified Synthesizer.Plain.Wave   as WaveL
import Synthesizer.Interpolation (marginNumber, )

import qualified Synthesizer.Causal.Oscillator as OsciC
import qualified Synthesizer.Causal.Process as Causal

import qualified Synthesizer.State.Signal as SigS

import qualified Synthesizer.Basic.Wave           as Wave
import qualified Synthesizer.Basic.Phase          as Phase

import qualified Test.Sound.Synthesizer.Plain.Interpolation as InterpolationTest

import Test.QuickCheck (test, Property, (==>), )
import Test.Utility (ArbChar, )
-- import Debug.Trace (trace, )

import qualified Number.NonNegative       as NonNeg

-- import qualified Algebra.RealTranscendental    as RealTrans
-- import qualified Algebra.Module                as Module
import qualified Algebra.RealField             as RealField
-- import qualified Algebra.Field                 as Field
-- import qualified Algebra.Ring                  as Ring
import qualified Algebra.Additive              as Additive


import Data.List.HT (viewL, takeWhileJust, )
import Data.Tuple.HT (mapSnd, )
import qualified Data.List as List


import NumericPrelude
import PreludeBase
import Prelude ()


limitMinRelativeValues ::
   Int -> Int -> [NonNeg.Int] -> Bool
limitMinRelativeValues xMin x0 xsnn =
   let xs = map NonNeg.toNumber xsnn
       (y0,limiter) = ToneModC.limitMinRelativeValues xMin x0
   in  (y0, Causal.applyGeneric limiter xs) ==
          ToneModL.limitMinRelativeValues xMin x0 xs

integrateFractional :: (RealField.C t) =>
   NonNeg.T t -> t -> Phase.T t -> [NonNeg.T t] -> [t] -> Property
integrateFractional
     periodNN shape0 phase shapesNN freqs =
   let shapes = map NonNeg.toNumber shapesNN
       period    = NonNeg.toNumber periodNN
       (c0, coordinator) =
          ToneModC.integrateFractional
             period (shape0, phase)
       coords =
          ToneModL.integrateFractional
             period (shape0, shapes) (phase, freqs)
   in  period /= zero  ==>
          c0 : Causal.applyGeneric coordinator (zip shapes freqs) ==
          coords

-- oscillatorCellSize :: (Show t, Show v, RealField.C t, Eq v) =>
oscillatorCellSize :: (RealField.C t, Eq v) =>
   Interpolation.Margin ->
   Interpolation.Margin ->
   NonNeg.Int -> NonNeg.T t ->
   NonNeg.Int -> InfiniteList v ->
   t -> t -> [NonNeg.T t] -> [t] ->
   Property
oscillatorCellSize
      marginLeap marginStep periodIntNN periodNN ext
      ixs shape0 phase shapesNN freqs =
   let shapes = map NonNeg.toNumber shapesNN
       period    = NonNeg.toNumber periodNN
       periodInt = NonNeg.toNumber periodIntNN
       len = minLengthMargin marginLeap marginStep periodInt ext
       tone = take len (listFromInfinite ixs)
       resampledTone =
          ToneModC.oscillatorCells
             marginLeap marginStep periodInt period tone
             (shape0, Phase.fromRepresentative phase)
          `Causal.applyGeneric`
          zip shapes freqs
   in  period /= zero  &&
       marginNumber marginLeap > zero &&
       marginNumber marginStep > zero  ==>
       all
          ((\cell ->
              Sig.lengthAtLeast (marginNumber marginLeap) cell &&
              all (Sig.lengthAtLeast (marginNumber marginStep))
                  (take (marginNumber marginLeap) cell))
           . SigS.toList . snd)
          resampledTone

oscillatorSuffixes :: (RealField.C t, Eq v) =>
   Interpolation.Margin ->
   Interpolation.Margin ->
   NonNeg.Int -> NonNeg.T t ->
   NonNeg.Int -> InfiniteList v ->
   t -> t -> [NonNeg.T t] -> [t] ->
   Property
oscillatorSuffixes
      marginLeap marginStep periodIntNN periodNN ext
      ixs shape0 phase shapesNN freqs =
   let shapes = map NonNeg.toNumber shapesNN
       period    = NonNeg.toNumber periodNN
       periodInt = NonNeg.toNumber periodIntNN
       len = minLengthMargin marginLeap marginStep periodInt ext
       tone = take len (listFromInfinite ixs)
       resampledToneA =
          init $
          map (\(sp,cell) ->
             (sp, takeWhileJust . map (fmap fst . viewL) $ cell)) $
          ToneModL.oscillatorSuffixes
             marginLeap marginStep periodInt period tone
             (shape0, shapes) (Phase.fromRepresentative phase, freqs)
       resampledToneB =
          ToneModC.oscillatorSuffixes
             marginLeap marginStep periodInt period tone
             (shape0, Phase.fromRepresentative phase)
          `Causal.applyGeneric`
          zip shapes freqs
   in  period /= zero  &&
       periodInt /= zero  &&
       marginNumber marginLeap > zero &&
       marginNumber marginStep > zero  ==>
          resampledToneA == resampledToneB

oscillatorCells :: (RealField.C t, Eq v) =>
   Interpolation.Margin ->
   Interpolation.Margin ->
   NonNeg.Int -> NonNeg.T t ->
   NonNeg.Int -> InfiniteList v ->
   t -> t -> [NonNeg.T t] -> [t] ->
   Property
oscillatorCells
      marginLeap marginStep periodIntNN periodNN ext
      ixs shape0 phase shapesNN freqs =
   let shapes = map NonNeg.toNumber shapesNN
       period    = NonNeg.toNumber periodNN
       periodInt = NonNeg.toNumber periodIntNN
       len = minLengthMargin marginLeap marginStep periodInt ext
       tone = take len (listFromInfinite ixs)
       resampledToneA =
          init $ map (mapSnd List.transpose) $
          ToneModL.oscillatorCells
             marginLeap marginStep periodInt period tone
             (shape0, shapes) (Phase.fromRepresentative phase, freqs)
       resampledToneB =
          map (mapSnd SigS.toList) $
          ToneModC.oscillatorCells
             marginLeap marginStep periodInt period tone
             (shape0, Phase.fromRepresentative phase)
          `Causal.applyGeneric`
          zip shapes freqs
   in  period /= zero  &&
       periodInt /= zero  &&
       marginNumber marginLeap > zero &&
       marginNumber marginStep > zero  ==>
          resampledToneA == resampledToneB
{-
Margin {marginNumber = 1, marginOffset = 2}
Margin {marginNumber = 5, marginOffset = 5}
3 % 4
0
('\DEL',['~','~','"'])
-2 % 5
2 % 5
[2 % 1,3 % 4]
[-5 % 2,-1 % 2]
-}

{- |
'WaveL.sampledTone' and 'WaveG.sampledTone'
do not only differ in the signal types they process,
but also in the way they order the signal values.
The cells for 'WaveL.sampledTone' are transposed
with respect to 'WaveG.sampledTone'.
-}
sampledTone :: (RealField.C a, Eq v) =>
   InterpolationTest.T a v ->
   InterpolationTest.T a v ->
   NonNeg.T a -> NonNeg.Int -> InfiniteList v ->
   a -> Phase.T a -> Property
sampledTone =
   InterpolationTest.use2 $ \ ipLeap ipStep
         periodNN ext ixs shape phase ->
   let period = NonNeg.toNumber periodNN
       periodInt = round period
       len = minLength ipLeap ipStep periodInt ext
       tone = take len (listFromInfinite ixs)
   in  period /= zero ==>
          WaveG.sampledTone ipLeap ipStep period tone shape `Wave.apply` phase ==
          WaveL.sampledTone ipLeap ipStep period tone shape `Wave.apply` phase



shapeFreqModFromSampledTone :: (RealField.C t, Eq v) =>
   InterpolationTest.T t v ->
   InterpolationTest.T t v ->
   NonNeg.T t ->
   NonNeg.Int -> InfiniteList v ->
   t -> Phase.T t -> [NonNeg.T t] -> [t] ->
   Property
shapeFreqModFromSampledTone =
   InterpolationTest.use2 $ \ ipLeap ipStep
         periodNN ext ixs shape0 phase shapesNN freqs ->
   let shapes = map NonNeg.toNumber shapesNN
       period = NonNeg.toNumber periodNN
       periodInt = round period
       len = minLength ipLeap ipStep periodInt ext
       tone = take len (listFromInfinite ixs)
       resampledToneA =
          init $
          Osci.shapeFreqModFromSampledTone ipLeap ipStep period tone
             shape0 (Phase.toRepresentative phase) shapes freqs
       resampledToneB =
          OsciC.shapeFreqModFromSampledTone
             ipLeap ipStep period tone shape0 phase
          `Causal.applyGeneric`
          zip shapes freqs
   in  period /= zero  ==>
          resampledToneA == resampledToneB


{-
We have a problem here with the phase distortion signal,
because frequency and shape modulation control signals
are delayed by one element with respect to the phase distortion.
How can we cope with different lengths of the control signals,
without padding the phase control with zeros?
This one did not work:
   phaseDistorts = pd:pds
   resampledToneA =
      Osci.shapePhaseFreqModFromSampledTone ipLeap ipStep period tone
         shape0 (Phase.toRepresentative phase) shapes (init phaseDistorts) freqs
-}
shapePhaseFreqModFromSampledTone :: (RealField.C t, Eq v) =>
   InterpolationTest.T t v ->
   InterpolationTest.T t v ->
   NonNeg.T t ->
   NonNeg.Int -> InfiniteList v ->
   t -> Phase.T t -> [NonNeg.T t] -> (t,[t]) -> [t] ->
   Property
shapePhaseFreqModFromSampledTone =
   InterpolationTest.use2 $ \ ipLeap ipStep
         periodNN ext ixs shape0 phase shapesNN (pd,pds) freqs ->
   let period = NonNeg.toNumber periodNN
       periodInt = round period
       len = minLength ipLeap ipStep periodInt ext
       tone = take len (listFromInfinite ixs)
       shapes = map NonNeg.toNumber shapesNN
       phaseDistorts = pd:pds ++ repeat zero
       resampledToneA =
          init $
          Osci.shapePhaseFreqModFromSampledTone ipLeap ipStep period tone
             shape0 (Phase.toRepresentative phase) shapes phaseDistorts freqs
       resampledToneB =
          OsciC.shapePhaseFreqModFromSampledTone
             ipLeap ipStep period tone shape0 phase
          `Causal.applyGeneric`
          zip3 shapes phaseDistorts freqs
   in  period /= zero  ==>
          resampledToneA == resampledToneB



tests :: [(String, IO ())]
tests =
   ("limitMinRelativeValues", test limitMinRelativeValues) :
   ("integrateFractional",
      test (\period -> integrateFractional (period :: NonNeg.Rational))) :
   ("oscillatorCellSize",
      test (\ml ms periodInt period ext ixs ->
               oscillatorCellSize ml ms periodInt (period :: NonNeg.Rational)
                  ext (ixs :: InfiniteList ArbChar))) :
   ("oscillatorSuffixes",
      test (\ml ms periodInt period ext ixs ->
               oscillatorSuffixes ml ms periodInt (period :: NonNeg.Rational)
                  ext (ixs :: InfiniteList ArbChar))) :
   ("oscillatorCells",
      test (\ml ms periodInt period ext ixs ->
               oscillatorCells ml ms periodInt (period :: NonNeg.Rational)
                  ext (ixs :: InfiniteList ArbChar))) :
   ("sampledTone",
      testRationalIp sampledTone) :
   ("shapeFreqModFromSampledTone",
      testRationalIp shapeFreqModFromSampledTone) :
   ("shapePhaseFreqModFromSampledTone",
      testRationalIp shapePhaseFreqModFromSampledTone) :
   []