{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
-- {-# LANGUAGE TypeFamilies, NoMonoLocalBinds #-}

{-# LANGUAGE NoIncoherentInstances #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE NoUndecidableInstances #-}

module Vivid.UGens.Maths (
     clip
---   , fold -- rename cuz of haskell clashes!
   , inRange
--   , inRect
     -- In Vivid.UGens.Filters:
   -- , integrator
   , leastChange
   , linExp
   , linLin
---   , modDif
   , mostChange
   , mulAdd
---   , runningMax
---   , runningMin
---   , runningSum
---   , schmidt
     -- In Vivid.UGens.Analysis:
   -- , slope
---   , wrap
   ) where

import Vivid.SC.SynthDef.Types (CalculationRate(..))
import Vivid.SynthDef
import Vivid.SynthDef.FromUA
-- import Vivid.UGens.Algebraic
import Vivid.UGens.Args
import Vivid.UGens.Algebraic

import qualified Data.ByteString.UTF8 as UTF8 (fromString)
import Data.Proxy

-- | 
--   **Note this has different behavior than 0.1!**
--   \"lo\" is not implied by
--   \"hi\".
-- 
--   If you know you always want \"lo\" to be negative \"hi\" (like -1 to 1)
--   you can use @'biOp' 'Clip2'@
clip :: (Args '["in"] '["lo", "hi"] a) => a -> SDBody a Signal
clip :: a -> SDBody a Signal
clip = String
-> CalculationRate
-> Vs '["in", "lo", "hi"]
-> (UA "lo" (SDBodyArgs a), UA "hi" (SDBodyArgs a))
-> a
-> SDBody a Signal
forall (tags :: [Symbol]) optional userSupplied (args :: [Symbol]).
(GetSymbolVals (Vs tags), FromUA optional, FromUA userSupplied,
 SDBodyArgs optional ~ SDBodyArgs userSupplied,
 SDBodyArgs optional ~ args) =>
String
-> CalculationRate
-> Vs tags
-> optional
-> userSupplied
-> SDBody' args Signal
makeUGen
   String
"Clip" CalculationRate
AR
   (Vs '["in", "lo", "hi"]
forall (a :: [Symbol]). Vs a
Vs::Vs '["in","lo","hi"])
   (Float -> UA "lo" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "lo" as
lo_ (Float
0::Float), Float -> UA "hi" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "hi" as
hi_ (Float
1::Float))

--- fold ::
--- fold =

-- | Returns 1.0 if \"in\" is between \"lo\" and \"hi\" (including
--   equalling "lo" or "hi"). Else 0.0.
-- 
--   Can be audio rate (AR), control rate (KR), or fixed (IR)
-- 
--   Lo and hi default to 0 and 1
inRange :: (Args '["in"] '["lo", "hi"] a) => a -> SDBody a Signal
inRange :: a -> SDBody a Signal
inRange = String
-> CalculationRate
-> Vs '["in", "lo", "hi"]
-> (UA "lo" (SDBodyArgs a), UA "hi" (SDBodyArgs a))
-> a
-> SDBody a Signal
forall (tags :: [Symbol]) optional userSupplied (args :: [Symbol]).
(GetSymbolVals (Vs tags), FromUA optional, FromUA userSupplied,
 SDBodyArgs optional ~ SDBodyArgs userSupplied,
 SDBodyArgs optional ~ args) =>
String
-> CalculationRate
-> Vs tags
-> optional
-> userSupplied
-> SDBody' args Signal
makeUGen
   String
"InRange" CalculationRate
AR
   (Vs '["in", "lo", "hi"]
forall (a :: [Symbol]). Vs a
Vs::Vs '["in", "lo", "hi"])
   (Float -> UA "lo" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "lo" as
lo_ (Float
0::Float), Float -> UA "hi" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "hi" as
hi_ (Float
1::Float))

--- -- inRect =

-- | Returns the value of whichever of the 2 signals is changing
--   least
-- 
--   Its default calculation rate is the highest of its 2 inputs
leastChange :: (ToSig s0 as, ToSig s1 as) => s0 -> s1 -> SDBody' as Signal
leastChange :: s0 -> s1 -> SDBody' as Signal
leastChange = String -> s0 -> s1 -> SDBody' as Signal
forall s0 (as :: [Symbol]) s1.
(ToSig s0 as, ToSig s1 as) =>
String -> s0 -> s1 -> SDBody' as Signal
leastOrMostChange String
"LeastChange"

leastOrMostChange :: (ToSig s0 as, ToSig s1 as) => String -> s0 -> s1 -> SDBody' as Signal
leastOrMostChange :: String -> s0 -> s1 -> SDBody' as Signal
leastOrMostChange String
ugenName s0
s0 s1
s1 = do
   Signal
s0' <- s0 -> SDBody' as Signal
forall s (args :: [Symbol]).
ToSig s args =>
s -> SDBody' args Signal
toSig s0
s0
   Signal
s1' <- s1 -> SDBody' as Signal
forall s (args :: [Symbol]).
ToSig s args =>
s -> SDBody' args Signal
toSig s1
s1
   CalculationRate
calcRate <- (forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum::Ord a=>[a]->a) ([CalculationRate] -> CalculationRate)
-> StateT
     ([Int], SynthDef as, VarSet as) Identity [CalculationRate]
-> StateT ([Int], SynthDef as, VarSet as) Identity CalculationRate
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [StateT ([Int], SynthDef as, VarSet as) Identity CalculationRate]
-> StateT
     ([Int], SynthDef as, VarSet as) Identity [CalculationRate]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence ((Signal
 -> StateT ([Int], SynthDef as, VarSet as) Identity CalculationRate)
-> [Signal]
-> [StateT
      ([Int], SynthDef as, VarSet as) Identity CalculationRate]
forall a b. (a -> b) -> [a] -> [b]
map Signal
-> StateT ([Int], SynthDef as, VarSet as) Identity CalculationRate
forall (args :: [Symbol]). Signal -> SDBody' args CalculationRate
getCalcRate [Signal
s0', Signal
s1'])
   UGen -> SDBody' as Signal
forall (args :: [Symbol]). UGen -> SDBody' args Signal
addUGen (UGen -> SDBody' as Signal) -> UGen -> SDBody' as Signal
forall a b. (a -> b) -> a -> b
$ UGenName -> CalculationRate -> [Signal] -> Int -> UGen
UGen (ByteString -> UGenName
UGName_S (ByteString -> UGenName)
-> (String -> ByteString) -> String -> UGenName
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString
UTF8.fromString (String -> UGenName) -> String -> UGenName
forall a b. (a -> b) -> a -> b
$ String
ugenName) CalculationRate
calcRate [Signal
s0',Signal
s1'] Int
1

-- | "Converts a linear range of values to an exponential range of values."
-- 
--   Args:
-- 
--   * *in* -  The input signal to convert.
-- 
--   * *srclo* - Lower limit of input range.
-- 
--   * *srchi* - Upper limit of input range.
-- 
--   * *dstlo* - Lower limit of output range.
-- 
--   * *dsthi* - Upper limit of output range.
-- 
--   **"The dstlo and dsthi arguments must be nonzero and have the same sign."**
-- 
--   This will have the same calculation rate as its \"in\" argument
linExp :: Args '["in"] '["srclo", "srchi", "dstlo", "dsthi"] a => a -> SDBody a Signal
linExp :: a -> SDBody a Signal
linExp a
as = do
   Signal
in' <- a
as a -> Proxy "in" -> SDBody a Signal
forall as (aToLookUp :: Symbol) (proxy :: Symbol -> *).
(FromUA as, Elem aToLookUp (UAsArgs as), KnownSymbol aToLookUp) =>
as -> proxy aToLookUp -> SDBody as Signal
`uaArgVal` (Proxy "in"
forall k (t :: k). Proxy t
Proxy::Proxy "in")
   Signal -> SDBody' (SDBodyArgs a) CalculationRate
forall (args :: [Symbol]). Signal -> SDBody' args CalculationRate
getCalcRate Signal
in' SDBody' (SDBodyArgs a) CalculationRate
-> (CalculationRate -> SDBody a Signal) -> SDBody a Signal
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
      CalculationRate
AR -> CalculationRate -> SDBody a Signal
successGraph CalculationRate
AR
      CalculationRate
KR -> CalculationRate -> SDBody a Signal
successGraph CalculationRate
KR
      CalculationRate
_ -> String -> SDBody a Signal
forall a. HasCallStack => String -> a
error String
"linExp: 'in' value must be at AR/KR"
 where
   successGraph :: CalculationRate -> SDBody a Signal
successGraph CalculationRate
calcRate = ((a -> SDBody a Signal) -> a -> SDBody a Signal
forall a b. (a -> b) -> a -> b
$ a
as) ((a -> SDBody a Signal) -> SDBody a Signal)
-> (a -> SDBody a Signal) -> SDBody a Signal
forall a b. (a -> b) -> a -> b
$ String
-> CalculationRate
-> Vs '["in", "srclo", "srchi", "dstlo", "dsthi"]
-> (UA "srclo" (SDBodyArgs a), UA "srchi" (SDBodyArgs a),
    UA "dstlo" (SDBodyArgs a), UA "dsthi" (SDBodyArgs a))
-> a
-> SDBody a Signal
forall (tags :: [Symbol]) optional userSupplied (args :: [Symbol]).
(GetSymbolVals (Vs tags), FromUA optional, FromUA userSupplied,
 SDBodyArgs optional ~ SDBodyArgs userSupplied,
 SDBodyArgs optional ~ args) =>
String
-> CalculationRate
-> Vs tags
-> optional
-> userSupplied
-> SDBody' args Signal
makeUGen
      String
"LinExp" CalculationRate
calcRate
      (Vs '["in", "srclo", "srchi", "dstlo", "dsthi"]
forall (a :: [Symbol]). Vs a
Vs::Vs '["in", "srclo", "srchi", "dstlo", "dsthi"])
      (Float -> UA "srclo" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "srclo" as
srclo_ (Float
0::Float), Float -> UA "srchi" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "srchi" as
srchi_ (Float
1::Float), Float -> UA "dstlo" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "dstlo" as
dstlo_ (Float
1::Float), Float -> UA "dsthi" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "dsthi" as
dsthi_ (Float
2::Float))


-- | "Converts a linear range of values to an another linear range of values."
-- 
--   Args:
-- 
--   * *in* -  The input signal to convert.
-- 
--   * *srclo* - Lower limit of input range.
-- 
--   * *srchi* - Upper limit of input range.
-- 
--   * *dstlo* - Lower limit of output range.
-- 
--   * *dsthi* - Upper limit of output range.
-- 
--   This will have the same calculation rate as its \"in\" argument
linLin :: Args '["in"] '["srclo", "srchi", "dstlo", "dsthi"] a => a -> SDBody a Signal
linLin :: a -> SDBody a Signal
linLin a
as = do
   Signal
srclo <- Float -> a -> Proxy "srclo" -> SDBody a Signal
forall as (aToLookUp :: Symbol) defaultVal (proxy :: Symbol -> *).
(FromUA as, KnownSymbol aToLookUp,
 ToSig defaultVal (SDBodyArgs as)) =>
defaultVal -> as -> proxy aToLookUp -> SDBody as Signal
uaArgValWDefault (-Float
1::Float) a
as (Proxy "srclo"
forall k (t :: k). Proxy t
Proxy::Proxy "srclo")
   Signal
srchi <- Float -> a -> Proxy "srchi" -> SDBody a Signal
forall as (aToLookUp :: Symbol) defaultVal (proxy :: Symbol -> *).
(FromUA as, KnownSymbol aToLookUp,
 ToSig defaultVal (SDBodyArgs as)) =>
defaultVal -> as -> proxy aToLookUp -> SDBody as Signal
uaArgValWDefault (Float
1::Float) a
as (Proxy "srchi"
forall k (t :: k). Proxy t
Proxy::Proxy "srchi")
   Signal
dstlo <- Float -> a -> Proxy "dstlo" -> SDBody a Signal
forall as (aToLookUp :: Symbol) defaultVal (proxy :: Symbol -> *).
(FromUA as, KnownSymbol aToLookUp,
 ToSig defaultVal (SDBodyArgs as)) =>
defaultVal -> as -> proxy aToLookUp -> SDBody as Signal
uaArgValWDefault (Float
0::Float) a
as (Proxy "dstlo"
forall k (t :: k). Proxy t
Proxy::Proxy "dstlo")
   Signal
dsthi <- Float -> a -> Proxy "dsthi" -> SDBody a Signal
forall as (aToLookUp :: Symbol) defaultVal (proxy :: Symbol -> *).
(FromUA as, KnownSymbol aToLookUp,
 ToSig defaultVal (SDBodyArgs as)) =>
defaultVal -> as -> proxy aToLookUp -> SDBody as Signal
uaArgValWDefault (Float
1::Float) a
as (Proxy "dsthi"
forall k (t :: k). Proxy t
Proxy::Proxy "dsthi")
   Signal
in' <- a
as a -> Proxy "in" -> SDBody a Signal
forall as (aToLookUp :: Symbol) (proxy :: Symbol -> *).
(FromUA as, Elem aToLookUp (UAsArgs as), KnownSymbol aToLookUp) =>
as -> proxy aToLookUp -> SDBody as Signal
`uaArgVal` (Proxy "in"
forall k (t :: k). Proxy t
Proxy::Proxy "in")
   let 
      scale :: SDBody' a Signal
scale  = (Signal
dsthi Signal -> Signal -> SDBody' a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~- Signal
dstlo) SDBody' a Signal -> SDBody' a Signal -> SDBody' a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~/ (Signal
srchi Signal -> Signal -> SDBody' a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~- Signal
srclo)
      offset :: SDBody' a Signal
offset = Signal
dstlo Signal -> SDBody' a Signal -> SDBody' a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~- (SDBody' a Signal
forall (a :: [Symbol]). SDBody' a Signal
scale SDBody' a Signal -> Signal -> SDBody' a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~* Signal
srclo)
   Signal -> SDBody' (SDBodyArgs a) CalculationRate
forall (args :: [Symbol]). Signal -> SDBody' args CalculationRate
getCalcRate Signal
in' SDBody' (SDBodyArgs a) CalculationRate
-> (CalculationRate -> SDBody a Signal) -> SDBody a Signal
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
         CalculationRate
AR -> (UA "in" (SDBodyArgs a), UA "mul" (SDBodyArgs a),
 UA "add" (SDBodyArgs a))
-> SDBody
     (UA "in" (SDBodyArgs a), UA "mul" (SDBodyArgs a),
      UA "add" (SDBodyArgs a))
     Signal
forall a. Args '["in"] '["mul", "add"] a => a -> SDBody a Signal
mulAdd (Signal -> UA "in" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "in" as
in_ Signal
in',  SDBody a Signal -> UA "mul" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "mul" as
mul_ SDBody a Signal
forall (a :: [Symbol]). SDBody' a Signal
scale, SDBody a Signal -> UA "add" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "add" as
add_ SDBody a Signal
forall (a :: [Symbol]). SDBody' a Signal
offset) 
         CalculationRate
KR -> (Signal
in' Signal -> SDBody a Signal -> SDBody a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~* SDBody a Signal
forall (a :: [Symbol]). SDBody' a Signal
scale) SDBody a Signal -> SDBody a Signal -> SDBody a Signal
forall i0 (a :: [Symbol]) i1.
(ToSig i0 a, ToSig i1 a) =>
i0 -> i1 -> SDBody' a Signal
~+ SDBody a Signal
forall (a :: [Symbol]). SDBody' a Signal
offset
         CalculationRate
_ -> String -> SDBody a Signal
forall a. HasCallStack => String -> a
error String
"linExp: 'in' value must be at AR/KR"

--- modDif ::
--- modDif =

-- | Opposite of 'leastChange'
mostChange :: (ToSig s0 as, ToSig s1 as) => s0 -> s1 -> SDBody' as Signal
mostChange :: s0 -> s1 -> SDBody' as Signal
mostChange = String -> s0 -> s1 -> SDBody' as Signal
forall s0 (as :: [Symbol]) s1.
(ToSig s0 as, ToSig s1 as) =>
String -> s0 -> s1 -> SDBody' as Signal
leastOrMostChange String
"MostChange"

mulAdd :: (Args '["in"] '["mul", "add"] a) => a -> SDBody a Signal
mulAdd :: a -> SDBody a Signal
mulAdd = String
-> CalculationRate
-> Vs '["in", "mul", "add"]
-> (UA "mul" (SDBodyArgs a), UA "add" (SDBodyArgs a))
-> a
-> SDBody a Signal
forall (tags :: [Symbol]) optional userSupplied (args :: [Symbol]).
(GetSymbolVals (Vs tags), FromUA optional, FromUA userSupplied,
 SDBodyArgs optional ~ SDBodyArgs userSupplied,
 SDBodyArgs optional ~ args) =>
String
-> CalculationRate
-> Vs tags
-> optional
-> userSupplied
-> SDBody' args Signal
makeUGen
   String
"MulAdd" CalculationRate
AR
   (Vs '["in", "mul", "add"]
forall (a :: [Symbol]). Vs a
Vs::Vs '["in", "mul", "add"])
   (Float -> UA "mul" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "mul" as
mul_ (Float
1::Float), Float -> UA "add" (SDBodyArgs a)
forall s (as :: [Symbol]). ToSig s as => s -> UA "add" as
add_ (Float
0::Float))

--- runningMax ::
--- runningMax =
--- runningMin ::
--- runningMin =
--- runningSum ::
--- runningSum =
--- schmidt ::
--- schmidt =
--- wrap ::
--- wrap =