csound-expression-0.0.3: src/CsoundExpr/Translator/Score.hs
module CsoundExpr.Translator.Score
(foldScores)
where
import Data.List
import Data.Function
import Temporal.Media(EventList(..))
import qualified Data.Map as Map
import CsoundExpr.Translator.ExprTree.Tree
import CsoundExpr.Translator.ExprTree.ExprTree
import CsoundExpr.Translator.ExprTree.ExprTree (Purity(..), exprTag)
import CsoundExpr.Translator.Cs.CsTree
import CsoundExpr.Translator.Cs.Utils
import CsoundExpr.Translator.Types
-- import Debug.Trace
-- debug msg x = trace (msg ++ " : " ++ show x) x
type NodeId = (Int, [Int])
type PfieldId = (Int, IsStringPfield)
type IsStringPfield = Bool
-- | all values in given instrument node
type NodeSlice = (NodeId, [ValueId])
foldScores :: EventList Dur Instr -> [(Instr, [Note])]
foldScores x = zip instrs vals
where instrs = zipWith substPfields ids rawInstrs
(ids, vals) = unzip $ map (foldVals . formValues) sco
rawInstrs = map (thd . head) sco
sco = scoreList x
--scoreList :: Events Instr -> [[(Time, Time, Instr)]]
--scoreList = groupBy ((liftPredicateToList equalStructure) `on` thd) .
-- sortBy (compare `on` thd) . toList
scoreList :: EventList Dur Instr -> [[(Time, Dur, Instr)]]
scoreList = iterScoreList [] . toList
where iterScoreList res s =
case s of
[] -> res
(x:xs) -> let (x1, x2) = partition (pred x) xs
in iterScoreList (res ++ [x : x1]) x2
pred = liftPredicateToList equalStructure `on` thd
toList (EventList _ xs) = xs
foldVals :: [(Time, Time, [(NodeId, ValueId)])] -> ([(NodeId, PfieldId)], [Note])
foldVals xs = (i, subst3 (valList v) xs)
where (i, v) = assignPfieldIds $ filter (isPfield . snd) $ alignValuesByNodes xs
subst3 ys xs = [(a, b, y) | (y, (a, b, _)) <- zip ys xs]
valList [] = repeat []
valList xs = transpose xs
isPfield :: [ValueId] -> Bool
isPfield = p . nub
where p x = case x of
(x0 : x1 : _) -> True
_ -> False
alignValuesByNodes :: [(Time, Time, [(NodeId, ValueId)])] -> [NodeSlice]
alignValuesByNodes = map select . transpose . map thd
where select x = (fst $ head x, map snd x)
substPfields :: [(NodeId, PfieldId)] -> Instr -> Instr
substPfields ids instr = foldl f instr ids -- zip ids [4..]
where f xs ((id0, id), n) =
substListElem id0 (substTreeById id (p n) (xs !! id0)) xs
p (id, isStr)
| isStr = mapType (const [S]) $ param id
| otherwise = mapType (const [I]) $ param id
substListElem :: Int -> a -> [a] -> [a]
substListElem id x xs = take id xs ++ [x] ++ drop (id+1) xs
formValues :: [(Time, Time, Instr)] -> [(Time, Time, [(NodeId, ValueId)])]
formValues = map f . sortBy p
where f (t, d, expr) = (t, d, toVals expr)
p (a1, b1, _) (a2, b2, _)
| a1 < a2 = LT
| a1 == a2 && a1 < a2 = LT
| a1 == a2 && a1 == a2 = EQ
| otherwise = GT
toVals :: Instr -> [(NodeId, ValueId)]
toVals = (addId0 =<< ) . zip [0..] . map getVals
where getVals = map (mapSnd value') . filter (isVal . exprOp . exprTag . snd) . toIdList
value' x = (exprPurity x, value $ exprOp $ exprTag x)
addId0 (id0, xs) = [((id0, ids), vs) | (ids, vs) <- xs]
assignPfieldIds :: [NodeSlice] -> ([(NodeId, PfieldId)], [[ValueId]])
assignPfieldIds xs = (zip (map fst xs) (zip ids isStr), orderValsWithIds ids vals)
where ids = map (+4) $ numerate $ map snd xs
isStr = map (isStringValueId . head . snd) xs
vals = map snd xs
orderValsWithIds :: [Int] -> [[ValueId]] -> [[ValueId]]
orderValsWithIds ids vals = map snd $ sortOn fst $ nubOn fst $ zip ids vals
sortOn :: Ord b => (a -> b) -> [a] -> [a]
sortOn f = sortBy (compare `on` f)
nubOn :: Eq b => (a -> b) -> [a] -> [a]
nubOn f = nubBy ((==) `on` f)
isStringValueId :: ValueId -> Bool
isStringValueId (_, v) = isString v
-- numerate :
-- assigns numbers to unique elements of list
-- (it's for (ExprTree -> ExprSeq) optimisation,
-- pfields with the same values should have same pfield ids)
numerate :: Ord b => [b] -> [Int]
numerate = fst . foldl f ([], Map.empty)
where f (s, m) b
| Map.member b m = (s ++ [m Map.! b], m)
| otherwise = (s ++ [id], mNew)
where id = Map.size m
mNew = Map.insert b id m