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

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

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

import qualified Synthesizer.Amplitude.Signal as SigV
import Synthesizer.Amplitude.Signal (toAmplitudeScalar)

-- import qualified Algebra.NormedSpace.Maximum as NormedMax
import qualified Algebra.OccasionallyScalar  as OccScalar
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, map)
-- import NumericPrelude
import Prelude ()


{- * dissection -}

unzip ::
   SigV.T y y' (yv0, yv1) ->
   (SigV.T y y' yv0, SigV.T y y' yv1)
unzip x =
   let (ss0,ss1) = List.unzip (SigV.samples x)
   in  (SigV.replaceSamples ss0 x, SigV.replaceSamples ss1 x)

unzip3 ::
   SigV.T y y' (yv0, yv1, yv2) ->
   (SigV.T y y' yv0, SigV.T y y' yv1, SigV.T y y' yv2)
unzip3 x =
   let (ss0,ss1,ss2) = List.unzip3 (SigV.samples x)
   in  (SigV.replaceSamples ss0 x, SigV.replaceSamples ss1 x, SigV.replaceSamples ss2 x)



{- * 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.
-}
concat ::
   (Ord y', Field.C y', OccScalar.C y y',
    Module.C y yv) =>
   [SigV.T y y' yv] -> SigV.T y y' yv
concat xs =
   concatVolume (List.maximum (map SigV.amplitude xs)) xs

{- |
Give the output volume explicitly.
Does also work for infinite lists.
-}
concatVolume ::
   (Field.C y', OccScalar.C y y',
    Module.C y yv) =>
   y' -> [SigV.T y y' yv] -> SigV.T y y' yv
concatVolume amp xs =
   let smps = map (SigV.vectorSamples (toAmplitudeScalar z)) xs
       z = SigV.Cons amp (List.concat smps)
   in  z


merge ::
   (Ord y', Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1) =>
   ([yv0] -> [yv1] -> [yv2]) ->
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' yv2
merge f x0 x1 =
   mergeVolume f (max (SigV.amplitude x0) (SigV.amplitude x1)) x0 x1

mergeVolume ::
   (Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1) =>
   ([yv0] -> [yv1] -> [yv2]) ->
   y' ->
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' yv2
mergeVolume f amp x y =
   let sampX = SigV.vectorSamples (toAmplitudeScalar z) x
       sampY = SigV.vectorSamples (toAmplitudeScalar z) y
       z = SigV.Cons amp (f sampX sampY)
   in  z


append ::
   (Ord y', Field.C y', OccScalar.C y y',
    Module.C y yv) =>
   SigV.T y y' yv -> SigV.T y y' yv -> SigV.T y y' yv
append = merge (List.++)

appendVolume ::
   (Field.C y', OccScalar.C y y',
    Module.C y yv) =>
   y' ->
   SigV.T y y' yv -> SigV.T y y' yv -> SigV.T y y' yv
appendVolume = mergeVolume (List.++)


zip ::
   (Ord y', Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1) =>
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' (yv0,yv1)
zip = merge List.zip

zipVolume ::
   (Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1) =>
   y' ->
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' (yv0,yv1)
zipVolume = mergeVolume List.zip



zip3 ::
   (Ord y', Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1, Module.C y yv2) =>
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' yv2 ->
   SigV.T y y' (yv0,yv1,yv2)
zip3 x0 x1 x2 =
   zip3Volume
      (SigV.amplitude x0 `max` SigV.amplitude x1 `max` SigV.amplitude x2)
      x0 x1 x2

zip3Volume ::
   (Field.C y', OccScalar.C y y',
    Module.C y yv0, Module.C y yv1, Module.C y yv2) =>
   y' ->
   SigV.T y y' yv0 -> SigV.T y y' yv1 -> SigV.T y y' yv2 ->
   SigV.T y y' (yv0,yv1,yv2)
zip3Volume amp x0 x1 x2 =
   let sampX0 = SigV.vectorSamples (toAmplitudeScalar z) x0
       sampX1 = SigV.vectorSamples (toAmplitudeScalar z) x1
       sampX2 = SigV.vectorSamples (toAmplitudeScalar z) x2
       z = SigV.Cons amp (List.zip3 sampX0 sampX1 sampX2)
   in  z