packages feed

csound-expression-dynamic-0.3.9.1: src/Csound/Dynamic/Types/Dep.hs

{-# Language CPP #-}
-- | Dependency tracking
module Csound.Dynamic.Types.Dep(
    DepT(..), LocalHistory(..), runDepT, execDepT, evalDepT,
    -- * Dependencies
    depT, depT_, mdepT, stripDepT, stmtOnlyT, toBlock, depends,

    -- * Variables
    newLocalVar, newLocalVars,
    writeVar, readVar, readOnlyVar, initVar, appendVarBy,

    -- * Arrays
    newLocalArrVar, newTmpArrVar,
    readArr, readOnlyArr, writeArr, writeInitArr, initArr, appendArrBy,

    -- * Read macros
    readMacrosDouble, readMacrosInt, readMacrosString,
    initMacrosDouble, initMacrosString, initMacrosInt
) where

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif

import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict
import Control.Monad(ap, liftM, zipWithM_)
import Data.Default

import Data.Fix(Fix(..))
import Data.Text (Text)
import Data.Text qualified as Text

import Csound.Dynamic.Types.Exp

-- | Csound's synonym for 'IO'-monad. 'Dep' means Side Effect.
-- You will bump into 'Dep' trying to read and write to delay lines,
-- making random signals or trying to save your audio to file.
-- Instrument is expected to return a value of @Dep [Sig]@.
-- So it's okay to do some side effects when playing a note.
newtype DepT m a = DepT { unDepT :: StateT LocalHistory m a }

data LocalHistory = LocalHistory
    { expDependency :: !E
    , newLineNum    :: !Int
    , newLocalVarId :: !Int }

instance Default LocalHistory where
    def = LocalHistory (noRate Starts) 0 0

instance Monad m => Functor (DepT m) where
    fmap = liftM

instance Monad m => Applicative (DepT m) where
    pure = DepT . return
    (<*>) = ap

instance Monad m => Monad (DepT m) where
    ma >>= mf = -- DepT $ unDepT ma >>= unDepT . mf
      DepT $ StateT $ \s -> do
        (aE, aS) <- runStateT (unDepT ma) (startSt s)
        (bE, bS) <- runStateT (unDepT (mf aE)) (startSt aS)
        pure (bE, setDeps bS aS)
      where
        startSt s = s
          { expDependency = rehashE $ Fix $ (unFix $ noRate Starts) { ratedExpDepends = Just (newLineNum s) }
          , newLineNum = succ $ newLineNum s
          }

        setDeps bS aS = bS
          { expDependency = depends (expDependency aS) (expDependency bS)
          , newLineNum = succ $ newLineNum bS
          }


{-
ifT1 :: Monad m => IfRate -> E -> DepT m (CodeBlock E) -> DepT m E
ifT1 ifRate check (DepT th) = DepT $ StateT $ \s -> do
  (_thE, thS)  <- runStateT th s
  let thDeps = expDependency thS
      a  = noRate $ IfBlock ifRate (condInfo $ setIfRate ifRate check) (CodeBlock $ PrimOr $ Right thDeps)
      a1 = rehashE $ Fix $ (unFix a) { ratedExpDepends = Just (newLineNum thS) }
      s1 = thS
            { newLineNum = succ $ newLineNum thS
            , expDependency = a1
            -- depends (expDependency thS) (depends (expDependency elS) a1)
            }
  pure (a1, s1)
-}
instance MonadTrans DepT where
    lift ma = DepT $ lift ma

runDepT :: (Functor m, Monad m) => DepT m a -> m (a, LocalHistory)
runDepT a = runStateT (unDepT $ a) def

evalDepT :: (Functor m, Monad m) => DepT m a -> m a
evalDepT a = evalStateT (unDepT $ a) def

execDepT :: (Functor m, Monad m) => DepT m () -> m E
execDepT a = fmap expDependency $ execStateT (unDepT $ a) def

-- dependency tracking

depends :: E -> E -> E
depends a1 a2 =
  case ratedExpExp (unFix a2) of
    Starts -> a1
    _ ->
      case ratedExpExp (unFix a1) of
        Starts -> a2
        _      -> noRate $ Seq (toPrimOr a1) (toPrimOr a2)

depT :: Monad m => E -> DepT m E
depT a = DepT $ do
    s <- get
    let a1 = rehashE $ Fix $ (unFix a) { ratedExpDepends = Just (newLineNum s) }
    put $ s {
        newLineNum = succ $ newLineNum s -- ,
        -- expDependency = depends (expDependency s) a1
        }
    return a1

depT_ :: (Monad m) => E -> DepT m ()
depT_ a = -- fmap (const ()) . depT
  DepT $ do
    s <- get
    let a1 = rehashE $ Fix $ (unFix a) { ratedExpDepends = Just (newLineNum s) }
    put $ s {
        newLineNum = succ $ newLineNum s,
        expDependency = depends (expDependency s) a1
        }
    return ()


toBlock :: Monad m => DepT m () -> DepT m (CodeBlock E)
toBlock (DepT act) = DepT $ do
  act
  CodeBlock <$> gets expDependency

mdepT :: (Monad m) => MultiOut [E] -> MultiOut (DepT m [E])
mdepT mas = \n -> mapM depT $ ( $ n) mas

stripDepT :: Monad m => DepT m a -> m a
stripDepT (DepT a) = evalStateT a def

stmtOnlyT :: Monad m => Exp E -> DepT m ()
stmtOnlyT stmt = depT_ $ noRate stmt

-- local variables

newLocalVars :: Monad m => [Rate] -> m [E] -> DepT m [Var]
newLocalVars rs vs = do
    vars <- mapM newVar rs
    zipWithM_ initVar vars =<< lift vs
    return vars

newLocalVar :: Monad m => Rate -> m E -> DepT m Var
newLocalVar rate val = do
    var <- newVar rate
    initVar var =<< lift val
    return var

newVar :: Monad m => Rate -> DepT m Var
newVar rate = DepT $ do
    s <- get
    let v = Var LocalVar rate (Text.pack $ show $ newLocalVarId s)
    put $ s { newLocalVarId = succ $ newLocalVarId s }
    return v

--------------------------------------------------
-- variables

-- generic funs

writeVar :: Monad m => Var -> E -> DepT m ()
writeVar v x = depT_ $ noRate $ WriteVar v $ toPrimOr x

readVar :: Monad m => Var -> DepT m E
readVar v = depT $ noRate $ ReadVar v

readOnlyVar :: Var -> E
readOnlyVar v = noRate $ ReadVar v

initVar :: Monad m => Var -> E -> DepT m ()
initVar v x = depT_ $ noRate $ InitVar v $ toPrimOr $ setRate Ir x

appendVarBy :: Monad m => (E -> E -> E) -> Var -> E -> DepT m ()
appendVarBy op v x = writeVar v . op x =<< readVar v

--------------------------------------------------
-- arrays

-- init

newLocalArrVar :: Monad m => Rate -> m [E] -> DepT m Var
newLocalArrVar rate val = do
    var <- newVar rate
    initArr var =<< lift val
    return var

newTmpArrVar :: Monad m => Rate -> DepT m Var
newTmpArrVar rate = newVar rate

-- ops

readArr :: Monad m => Var -> [E] -> DepT m E
readArr v ixs = depT $ noRate $ ReadArr v (fmap toPrimOr ixs)

readOnlyArr :: Var -> [E] -> E
readOnlyArr v ixs = noRate $ ReadArr v (fmap toPrimOr ixs)

writeArr :: Monad m => Var -> [E] -> E -> DepT m ()
writeArr v ixs a = depT_ $ noRate $ WriteArr v (fmap toPrimOr ixs) (toPrimOr a)

writeInitArr :: Monad m => Var -> [E] -> E -> DepT m ()
writeInitArr v ixs a = depT_ $ noRate $ WriteInitArr v (fmap toPrimOr ixs) (toPrimOr a)

initArr :: Monad m => Var -> [E] -> DepT m ()
initArr v xs = depT_ $ noRate $ InitArr v $ fmap toPrimOr xs

appendArrBy :: Monad m => (E -> E -> E) -> Var -> [E] -> E -> DepT m ()
appendArrBy op v ixs x = writeArr v ixs . op x =<< readArr v ixs

--------------------------------------------------
-- read global macros arguments

readMacrosDouble :: Text -> E
readMacrosDouble = readMacrosBy ReadMacrosDouble Ir

readMacrosInt :: Text -> E
readMacrosInt = readMacrosBy ReadMacrosInt Ir

readMacrosString :: Text -> E
readMacrosString = readMacrosBy ReadMacrosString Sr

initMacrosDouble :: Monad m => Text -> Double -> DepT m ()
initMacrosDouble = initMacrosBy InitMacrosDouble

initMacrosString :: Monad m => Text -> Text -> DepT m ()
initMacrosString = initMacrosBy InitMacrosString

initMacrosInt :: Monad m => Text -> Int -> DepT m ()
initMacrosInt = initMacrosBy InitMacrosInt

readMacrosBy :: (Text -> Exp E) -> Rate -> Text -> E
readMacrosBy readMacro rate name = withRate rate $ readMacro name

initMacrosBy :: Monad m => (Text -> a -> Exp E) -> Text -> a -> DepT m ()
initMacrosBy maker name value = depT_ $ noRate $ maker name value