-- | 
-- Module: Fader
-- Description: Fades one signal to another
-- Copyright: (c) 2013 Tom Hawkins & Lee Pike
--
-- Fades one signal to another.

module Language.Atom.Common.Fader
  ( Fader
  , FaderInit (..)
  , fader
  , fadeToA
  , fadeToB
  , fadeToCenter
  ) where

import Language.Atom.Expressions
import Language.Atom.Language
import Data.Int (Int32)

-- | Fader object.
data Fader = Fader (V Int32)

-- | Fader initalization.
data FaderInit = OnA -- ^ Start at signal A
               | OnB -- ^ Start at signal B
               | OnCenter -- ^ Start at average of A and B

toA, toB, toCenter :: Int32
toA = 0
toB = 1
toCenter = 2

-- | Fader construction
fader :: Name -- ^ Name
         -> Double -- ^ Fade rate
         -> FaderInit -- ^ Initialization
         -> E Double -- ^ Signal A
         -> E Double -- ^ Signal B
         -> Atom (Fader, E Double)
fader name_ rate init_ a b = atom name_ $ do
  --assert "positiveRate" $ rate >= 0

  target <- int32 "target" $ case init_ of OnA -> toA
                                           OnB -> toB
                                           OnCenter -> toCenter
  perA <- double "perA" $ case init_ of OnA -> 1
                                        OnB -> 0
                                        OnCenter -> 0.5

  atom "toA" $ do
    cond $ value target ==. Const toA
    cond $ value perA <. 1
    perA <== mux (1 - value perA <. Const rate) 1 (value perA + Const rate)

  atom "toB" $ do
    cond $ value target ==. Const toB
    cond $ value perA >. 0
    perA <== mux (value perA <. Const rate) 0 (value perA - Const rate)

  atom "toCenterFrom0" $ do
    cond $ value target ==. Const toCenter
    cond $ value perA <. 0.5
    perA <== mux (0.5 - value perA <. Const rate) 0.5 (value perA + Const rate)

  atom "toCenterFrom1" $ do
    cond $ value target ==. Const toCenter
    cond $ value perA >. 0.5
    perA <== mux (value perA - 0.5 <. Const rate) 0.5 (value perA - Const rate)

  return (Fader target, (a * value perA + b * (1 - value perA)) / 2)

-- | Fade to signal A.
fadeToA :: Fader -> Atom ()
fadeToA (Fader target) = target <== Const toA

-- | Fade to signal B.
fadeToB :: Fader -> Atom ()
fadeToB (Fader target) = target <== Const toB

-- | Fade to center, i.e. average of signal A and B.
fadeToCenter :: Fader -> Atom ()
fadeToCenter (Fader target) = target <== Const toCenter