{- |
Copyright   :  (c) Henning Thielemann 2008
License     :  GPL

Maintainer  :  synthesizer@henning-thielemann.de
Stability   :  provisional
Portability :  requires multi-parameter type classes
-}
module Synthesizer.Dimensional.Amplitude.Cut (
   {- * dissection -}
   unzip,
   unzip3,
   leftFromStereo, rightFromStereo,

   {- * glueing -}
   concat,      concatVolume,
   append,      appendVolume,
   zip,         zipVolume,
   zip3,        zip3Volume,
   mergeStereo, mergeStereoVolume,
   selectBool,
  ) where

import qualified Synthesizer.Dimensional.Straight.Signal as SigS
import qualified Synthesizer.Dimensional.Amplitude.Signal as SigA
import Synthesizer.Dimensional.Amplitude.Signal (toAmplitudeScalar)

import qualified Synthesizer.State.Signal  as Sig

import qualified Synthesizer.Frame.Stereo as Stereo

import qualified Number.DimensionTerm        as DN
import qualified Algebra.DimensionTerm       as Dim

-- import Number.DimensionTerm ((&*&))

-- import qualified Algebra.NormedSpace.Maximum as NormedMax
import qualified Algebra.Module              as Module
import qualified Algebra.Field               as Field
-- import qualified Algebra.Ring                as Ring

import qualified Data.List as List

import PreludeBase (Ord, max, )
-- import NumericPrelude
import Prelude ()


{- * dissection -}

{-# INLINE unzip #-}
unzip :: (Dim.C u) =>
   SigA.R s u y (yv0, yv1) ->
   (SigA.R s u y yv0, SigA.R s u y yv1)
unzip x =
   let (ss0,ss1) = Sig.unzip (SigA.samples x)
   in  (SigA.replaceSamples ss0 x, SigA.replaceSamples ss1 x)

{-# INLINE unzip3 #-}
unzip3 :: (Dim.C u) =>
   SigA.R s u y (yv0, yv1, yv2) ->
   (SigA.R s u y yv0, SigA.R s u y yv1, SigA.R s u y yv2)
unzip3 x =
   let (ss0,ss1,ss2) = Sig.unzip3 (SigA.samples x)
   in  (SigA.replaceSamples ss0 x, SigA.replaceSamples ss1 x, SigA.replaceSamples ss2 x)


{-# INLINE leftFromStereo #-}
leftFromStereo :: (Dim.C u) =>
   SigA.R s u y (Stereo.T yv) -> SigA.R s u y yv
leftFromStereo = SigA.processSamples (Sig.map Stereo.left)

{-# INLINE rightFromStereo #-}
rightFromStereo :: (Dim.C u) =>
   SigA.R s u y (Stereo.T yv) -> SigA.R s u y yv
rightFromStereo = SigA.processSamples (Sig.map Stereo.right)



{- * glueing -}

{- |
Similar to @foldr1 append@ but more efficient and accurate,
because it reduces the number of amplifications.
Does not work for infinite lists,
because no maximum amplitude can be computed.
-}
{-# INLINE concat #-}
concat ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv) =>
   [SigA.R s u y yv] -> SigA.R s u y yv
concat xs =
   concatVolume (List.maximum (List.map SigA.amplitude xs)) xs

{- |
Give the output volume explicitly.
Does also work for infinite lists.
-}
{-# INLINE concatVolume #-}
concatVolume ::
   (Field.C y, Dim.C u,
    Module.C y yv) =>
   DN.T u y -> [SigA.R s u y yv] -> SigA.R s u y yv
concatVolume amp xs =
   let smps = List.map (SigA.vectorSamples (toAmplitudeScalar z)) xs
       z = SigA.fromSamples amp (Sig.concat smps)
   in  z


{-# INLINE merge #-}
merge ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1) =>
   (Sig.T yv0 -> Sig.T yv1 -> Sig.T yv2) ->
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y yv2
merge f x0 x1 =
   mergeVolume f (max (SigA.amplitude x0) (SigA.amplitude x1)) x0 x1

{-# INLINE mergeVolume #-}
mergeVolume ::
   (Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1) =>
   (Sig.T yv0 -> Sig.T yv1 -> Sig.T yv2) ->
   DN.T u y ->
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y yv2
mergeVolume f amp x y =
   let sampX = SigA.vectorSamples (toAmplitudeScalar z) x
       sampY = SigA.vectorSamples (toAmplitudeScalar z) y
       z = SigA.fromSamples amp (f sampX sampY)
   in  z


{-# INLINE append #-}
append ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv) =>
   SigA.R s u y yv -> SigA.R s u y yv -> SigA.R s u y yv
append = merge Sig.append

{-# INLINE appendVolume #-}
appendVolume ::
   (Field.C y, Dim.C u,
    Module.C y yv) =>
   DN.T u y ->
   SigA.R s u y yv -> SigA.R s u y yv -> SigA.R s u y yv
appendVolume = mergeVolume Sig.append


{-# INLINE zip #-}
zip ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1) =>
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y (yv0,yv1)
zip = merge Sig.zip

{-# INLINE zipVolume #-}
zipVolume ::
   (Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1) =>
   DN.T u y ->
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y (yv0,yv1)
zipVolume = mergeVolume Sig.zip



{-# INLINE mergeStereo #-}
mergeStereo ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv) =>
   SigA.R s u y yv -> SigA.R s u y yv -> SigA.R s u y (Stereo.T yv)
mergeStereo = merge (Sig.zipWith Stereo.cons)

{-# INLINE mergeStereoVolume #-}
mergeStereoVolume ::
   (Field.C y, Dim.C u,
    Module.C y yv) =>
   DN.T u y ->
   SigA.R s u y yv -> SigA.R s u y yv -> SigA.R s u y (Stereo.T yv)
mergeStereoVolume = mergeVolume (Sig.zipWith Stereo.cons)



{-# INLINE zip3 #-}
zip3 ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1, Module.C y yv2) =>
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y yv2 ->
   SigA.R s u y (yv0,yv1,yv2)
zip3 x0 x1 x2 =
   zip3Volume
      (SigA.amplitude x0 `max` SigA.amplitude x1 `max` SigA.amplitude x2)
      x0 x1 x2

{-# INLINE zip3Volume #-}
zip3Volume ::
   (Field.C y, Dim.C u,
    Module.C y yv0, Module.C y yv1, Module.C y yv2) =>
   DN.T u y ->
   SigA.R s u y yv0 -> SigA.R s u y yv1 -> SigA.R s u y yv2 ->
   SigA.R s u y (yv0,yv1,yv2)
zip3Volume amp x0 x1 x2 =
   let sampX0 = SigA.vectorSamples (toAmplitudeScalar z) x0
       sampX1 = SigA.vectorSamples (toAmplitudeScalar z) x1
       sampX2 = SigA.vectorSamples (toAmplitudeScalar z) x2
       z = SigA.fromSamples amp (Sig.zip3 sampX0 sampX1 sampX2)
   in  z


{-# INLINE selectBool #-}
selectBool ::
   (Ord y, Field.C y, Dim.C u,
    Module.C y yv) =>
   SigA.R s u y yv {- ^ False -} ->
   SigA.R s u y yv {- ^ True -} ->
   SigS.Binary s ->
   SigA.R s u y yv
selectBool xf xt cs =
   SigA.processSamples
      (Sig.zipWith (\c (xfi,xti) -> if c then xti else xfi) (SigS.toSamples cs))
      (zip xf xt)