-- | Optimisations of UGen graphs.
module Sound.SC3.UGen.Optimise where

import System.Random {- random -}

import Sound.SC3.Common.Math.Operator
import Sound.SC3.Common.Rate
import Sound.SC3.UGen.Type
import Sound.SC3.UGen.UGen

-- | Constant form of 'rand' UGen.
c_rand :: Random a => Int -> a -> a -> a
c_rand :: Int -> a -> a -> a
c_rand Int
z a
l a
r = (a, StdGen) -> a
forall a b. (a, b) -> a
fst ((a, a) -> StdGen -> (a, StdGen)
forall a g. (Random a, RandomGen g) => (a, a) -> g -> (a, g)
randomR (a
l,a
r) (Int -> StdGen
mkStdGen Int
z))

-- | Constant form of 'iRand' UGen.
c_irand :: (Num b, RealFrac a, Random a) => Int -> a -> a -> b
c_irand :: Int -> a -> a -> b
c_irand Int
z a
l a
r = Integer -> b
forall a. Num a => Integer -> a
fromInteger (a -> Integer
forall a b. (RealFrac a, Integral b) => a -> b
round (Int -> a -> a -> a
forall a. Random a => Int -> a -> a -> a
c_rand Int
z a
l a
r))

-- | Optimise 'UGen' graph by re-writing 'rand' and 'iRand' UGens that
-- have 'Constant' inputs.  This, of course, changes the nature of the
-- graph, it is no longer randomised at the server.  It's a useful
-- transformation for very large graphs which are being constructed
-- and sent each time the graph is played.
--
-- > import Sound.SC3.UGen.Dot {- hsc3-dot -}
--
-- > let u = sinOsc AR (rand 'a' 220 440) 0 * 0.1
-- > in draw (u + ugen_optimise_ir_rand u)
ugen_optimise_ir_rand :: UGen -> UGen
ugen_optimise_ir_rand :: UGen -> UGen
ugen_optimise_ir_rand =
    let f :: UGen -> UGen
f UGen
u =
            case UGen
u of
              Primitive_U Primitive
p ->
                  case Primitive
p of
                    Primitive Rate
IR [Char]
"Rand" [Constant_U (Constant Sample
l),Constant_U (Constant Sample
r)] [Rate
IR] Special
_ (UId Int
z) ->
                        Constant -> UGen
Constant_U (Sample -> Constant
Constant (Int -> Sample -> Sample -> Sample
forall a. Random a => Int -> a -> a -> a
c_rand Int
z Sample
l Sample
r))
                    Primitive Rate
IR [Char]
"IRand" [Constant_U (Constant Sample
l),Constant_U (Constant Sample
r)] [Rate
IR] Special
_ (UId Int
z) ->
                        Constant -> UGen
Constant_U (Sample -> Constant
Constant (Int -> Sample -> Sample -> Sample
forall b a. (Num b, RealFrac a, Random a) => Int -> a -> a -> b
c_irand Int
z Sample
l Sample
r))
                    Primitive
_ -> UGen
u
              UGen
_ -> UGen
u
    in (UGen -> Bool) -> (UGen -> UGen) -> UGen -> UGen
ugenTraverse (Bool -> UGen -> Bool
forall a b. a -> b -> a
const Bool
False) UGen -> UGen
f

-- | Optimise 'UGen' graph by re-writing binary operators with
-- 'Constant' inputs.  The standard graph constructors already do
-- this, however subsequent optimisations, ie. 'ugen_optimise_ir_rand'
-- can re-introduce these sub-graphs, and the /Plain/ graph
-- constructors are un-optimised.
--
-- > let u = constant
-- > u 5 * u 10 == u 50
-- > u 5 ==* u 5 == u 1
-- > u 5 >* u 4 == u 1
-- > u 5 <=* u 5 == u 1
-- > abs (u (-1)) == u 1
-- > u 5 / u 2 == u 2.5
--
-- > let {u = lfPulse AR (2 ** rand 'α' (-9) 1) 0 0.5
-- >     ;u' = ugen_optimise_ir_rand u}
-- > in draw (mix (mce [u,u',ugen_optimise_const_operator u']))
ugen_optimise_const_operator :: UGen -> UGen
ugen_optimise_const_operator :: UGen -> UGen
ugen_optimise_const_operator =
    let f :: UGen -> UGen
f UGen
u =
            case UGen
u of
              Primitive_U Primitive
p ->
                  case Primitive
p of
                    Primitive Rate
_ [Char]
"BinaryOpUGen" [Constant_U (Constant Sample
l)
                                               ,Constant_U (Constant Sample
r)] [Rate
_] (Special Int
z) UGenId
_ ->
                        case Int -> Maybe (Sample -> Sample -> Sample)
forall n. (RealFrac n, Floating n) => Int -> Maybe (n -> n -> n)
binop_special_hs Int
z of
                          Just Sample -> Sample -> Sample
fn -> Constant -> UGen
Constant_U (Sample -> Constant
Constant (Sample -> Sample -> Sample
fn Sample
l Sample
r))
                          Maybe (Sample -> Sample -> Sample)
_ -> UGen
u
                    Primitive Rate
_ [Char]
"UnaryOpUGen" [Constant_U (Constant Sample
i)] [Rate
_] (Special Int
z) UGenId
_ ->
                        case Int -> Maybe (Sample -> Sample)
forall n. (RealFrac n, Floating n) => Int -> Maybe (n -> n)
uop_special_hs Int
z of
                          Just Sample -> Sample
fn -> Constant -> UGen
Constant_U (Sample -> Constant
Constant (Sample -> Sample
fn Sample
i))
                          Maybe (Sample -> Sample)
_ -> UGen
u
                    Primitive
_ -> UGen
u
              UGen
_ -> UGen
u
    in (UGen -> Bool) -> (UGen -> UGen) -> UGen -> UGen
ugenTraverse (Bool -> UGen -> Bool
forall a b. a -> b -> a
const Bool
False) UGen -> UGen
f

-- | 'u_constant' of 'ugen_optimise_ir_rand'.
constant_opt :: UGen -> Maybe Sample
constant_opt :: UGen -> Maybe Sample
constant_opt = UGen -> Maybe Sample
u_constant (UGen -> Maybe Sample) -> (UGen -> UGen) -> UGen -> Maybe Sample
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UGen -> UGen
ugen_optimise_ir_rand