content: -- | **Note:** The argument format for these is a little
  --   rough, and is likely to change in the future
  
  {-# LANGUAGE
       DataKinds
     , LambdaCase
     , OverloadedStrings
     , ScopedTypeVariables
     , TypeFamilies, NoMonoLocalBinds
     , ViewPatterns
  
     , FlexibleContexts
  
     , NoIncoherentInstances
     , NoMonomorphismRestriction
     , NoUndecidableInstances
     #-}
  
  module Vivid.UGens.Envelopes (
       -- In UGens.Filters.Linear:
     -- decay
       -- In UGens.Filters.Linear:
     -- , decay2
       -- In Vivid.UGens.Demand:
     -- , demandEnvGen
       adsrGen
  ---   , dadsrGen
     , envGate
     , envGen
     , envGen_wGate
  ---   , iEnvGen
     , line
     , linen
     , percGen
     , xLine
     ) where
  
  import Vivid.Envelopes
  import Vivid.SC.SynthDef.Types (CalculationRate(..))
  import Vivid.SynthDef
  import Vivid.SynthDef.FromUA
  -- import Vivid.SynthDef.TypesafeArgs
  import Vivid.UGens.Args
  import Vivid.UGens.Algebraic
  
  import GHC.TypeLits
  -- import qualified Data.Map as Map
  import Data.Monoid
  import Data.Proxy
  
  -- | Defaults to 'AR'
  adsrGen :: (Args '[] '["peakLevel", {- "curve", -} "bias", "gate", "doneAction"] as, ToSig attackTime (SDBodyArgs as), ToSig decayTime (SDBodyArgs as), ToSig sustainLevel (SDBodyArgs as), ToSig releaseTime (SDBodyArgs as)) => attackTime -> decayTime -> sustainLevel -> releaseTime -> EnvCurve -> as -> SDBody as Signal
  adsrGen attackTime decayTime sustainLevel releaseTime curve userArgs = do
     attackTime' <- toSig attackTime
     decayTime' <- toSig decayTime
     sustainLevel' <- toSig sustainLevel
     releaseTime' <- toSig releaseTime
     peakLevel <- uaArgValWDefault (1::Float) userArgs (Proxy::Proxy "peakLevel")
     bias <- uaArgValWDefault (0::Float) userArgs (Proxy::Proxy "bias")
     doneAction <- uaArgValWDefault (0::Float) userArgs (Proxy::Proxy "doneAction")
     gate <- uaArgValWDefault (1::Float) userArgs (Proxy::Proxy "gate")
  
     peakXSustain <- peakLevel ~* sustainLevel'
     let plusBias :: (Signal, Signal) -> SDBody' a (Signal, Signal)
         plusBias (a, b) = do
            a' <- bias ~+ a
            return (a', b)
     biasPlusCurveSegs <- mapM plusBias [
             (peakLevel, attackTime')
           , (peakXSustain, decayTime')
           , (Constant 0, releaseTime')
           ]
  -- maybe write in terms of 'dadsr' ^^
     signals <- envLiterallyToSignals $ EnvLiterally {
          _envLiterally_initialVal = bias
        , _envLiterally_releaseNode = Just 2
        , _envLiterally_offset = 0
        , _envLiterally_loopNode = Nothing
        , _envLiterally_curveSegments =
           map (\(a,b)->EnvSegment a b curve) biasPlusCurveSegs
        }
     addUGen $ UGen (UGName_S "EnvGen") AR ([
          gate
        , Constant 1 -- levelScale
        , Constant 0 -- levelBias
        , Constant 1 -- timeScale
        , doneAction -- doneActionNum doneAction
        ] <> signals) 1
  
  --- dadsrGen ::
  --- dadsrGen =
  
  
  -- TODO: do we need 'fadeSecs' anymore? maybe we should just use 'releaseIn'...
  envGate :: Subset '["gate","fadeSecs"] a => SDBody' a Signal
  envGate = do
       -- Le -- less than or equal
     gate <- biOp Le (V::V "fadeSecs") (0::Float)
     let theEnv = EnvLiterally {
              _envLiterally_initialVal = gate
            , _envLiterally_releaseNode = Just 1
            , _envLiterally_offset = 0
            , _envLiterally_loopNode = Nothing
            , _envLiterally_curveSegments = [
                 EnvSegment (Constant 1) (Constant 1) Curve_Sin
               , EnvSegment (Constant 0) (Constant 1) Curve_Sin
               ]
            }
  
     envGen_wGate (V::V "gate") (V::V "fadeSecs") theEnv FreeEnclosing
  
  -- | Defaults to 'AR'
  envGen :: EnvLiterally a -> DoneAction -> SDBody' a Signal
  envGen theEnv doneAction = do
     curveSignals <- envLiterallyToSignals theEnv
     addUGen $ UGen (UGName_S "EnvGen") AR ([
          Constant 1 -- gate
        , Constant 1 -- levelScale
        , Constant 0 -- levelBias
        , Constant 1 -- timeScale
        , Constant $ doneActionNum doneAction
        ] <> curveSignals) 1
  
  envGen_wGate :: (ToSig gate a, ToSig timeScale a) => gate -> timeScale -> EnvLiterally a -> DoneAction -> SDBody' a Signal
  envGen_wGate gate timeScale theEnv doneAction = do
     gate' <- toSig gate
     timeScale' <- toSig timeScale
     curveSignals <- envLiterallyToSignals theEnv
     addUGen $ UGen (UGName_S "EnvGen") AR ([
          gate' -- Constant 1 -- gate
        , Constant 1 -- levelScale
        , Constant 0 -- levelBias
        , timeScale' -- , Constant 1 -- timeScale
        , Constant $ doneActionNum doneAction
        ] <> curveSignals) 1
  
  
  --- iEnvGen ::
  --- iEnvGen =
  
  -- | \"Generates a line from the start value to the end value.\"
  -- 
  --   Note this won't change after it's created, so if you'd like
  --   to e.g. be able to change the \"freq\" in
  -- 
  --   > line (start_ 0, end_ (V::V "freq"))
  -- 
  --   you should write
  -- 
  --   > (V::V "freq") ~* line (start_ 0, end_ 1)
  -- 
  --   instead.
  -- 
  --   Defaults to KR
  line :: (Args '[] '["start","end","duration","doneAction"] a) => a -> SDBody a Signal
  line = makeUGen
     "Line" AR
     (Vs::Vs '["start","end","duration","doneAction"])
     (start_ (0::Float), end_ (0::Float), duration_ (1::Float), doneAction_ (0::Float))
  
  -- | "Simple linear envelope generator"
  -- 
  --   Can't change after it's created -- see the note about 'line' if you want it to
  -- 
  --   Only computes at "KR"
  linen :: (Args '[] '["gate", "attackSecs", "susLevel", "releaseSecs", "doneAction"] a) => a -> SDBody a Signal
  linen = makeUGen
     "Linen" KR
     (Vs::Vs '["gate", "attackSecs", "susLevel", "releaseSecs", "doneAction"])
     (gate_ (1::Float), attackTime_ (0.01::Float), susLevel_ (1::Float), releaseTime_ (1::Float), doneAction_ (0::Float))
  
  
  
  -- | Percussive hit
  -- 
  --   'doneAction' is currently 2 but may either be 0 or 2 in future versions
  percGen :: (Args '[] '["attackSecs", "releaseSecs", "level", "curve", "doneAction"] a) => a -> SDBody a Signal
  percGen userArgs = do
     level <- uaArgWDef_onlyConst (1::Float) userArgs (V::V "level")
     attackTime <- uaArgWDef_onlyConst (0.01::Float) userArgs (V::V "attackSecs")
     releaseTime <- uaArgWDef_onlyConst (1::Float) userArgs (V::V "releaseSecs")
     curve <- uaArgWDef_onlyConst (-4::Float) userArgs (V::V "curve")
     doneAction <- fromEnum <$> uaArgWDef_onlyConst (2::Float) userArgs (V::V "doneAction")
  
     envGen (env 0 [(level, attackTime), (0, releaseTime)] (Curve_Curve curve)) (DoneAction_AsNum doneAction)
   where
     uaArgWDef_onlyConst defaultVal args argName =
        uaArgValWDefault defaultVal args argName >>= \case
           Constant x -> return x
           _ -> error $ "bad argument type: "<>show (symbolVal argName)<>" wasn't a Constant"
  
  -- | \"Generates an exponential curve from the start value to the end value. Both the start and end values must be non-zero and have the same sign.\"
  -- 
  -- Defaults to KR
  xLine :: (Args '[] '["start","end","duration","doneAction"] a) => a -> SDBody a Signal
  xLine = makeUGen
     "XLine" KR
     (Vs::Vs '["start","end","duration","doneAction"])
     (start_ (1::Float), end_ (2::Float), duration_ (1::Float), doneAction_ (0::Float))
  