module Csound.Render(
renderCsd, renderCsdBy, out, outs
) where
import Data.Default
import Data.Maybe(catMaybes)
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad.Trans.State(evalState)
import Data.Fix
import Csound.Exp
import Csound.Exp.Wrapper hiding (double, int)
import Csound.Tfm.TfmTree(tabMap)
import Csound.Render.Sco
import Csound.Render.Instr
import Csound.Render.Options
import Csound.Tfm.TfmTree(TabMap)
import Csound.Exp.Numeric
import Csound.Render.Pretty
import Csound.Opcode(clip, zeroDbfs)
out :: Sig -> Out
out = return . return
outs :: [Sig] -> Out
outs = return
renderCsd :: [SigOut] -> String
renderCsd = renderCsdBy def
renderCsdBy :: CsdOptions -> [SigOut] -> String
renderCsdBy opt as = show $ ppCsdFile
(renderFlags opt)
(renderInstr0 (nchnls lastInstrExp) (midiAssignTable ids as) opt)
(ppOrc $ firstInstr : lastInstr : zipWith (renderInstr krateSet fts) ids instrs)
(ppSco $ firstInstrNote : lastInstrNote : zipWith (renderScores strs fts) ids scos)
(renderStringTable strs)
(renderTotalDur $$ renderTabs fts)
where scos = map (scoSigOut' . sigOutContent) as
(instrs, effects, initOuts) = unzip3 $ zipWith runExpReader as ids
fts = tabMap $ lastInstrExp : instrs
strs = stringMap $ concat scos
ids = take nInstr [2 .. ]
nInstr = length as
firstInstrId = 1
lastInstrId = nInstr + 2
firstInstr = renderInstr krateSet fts firstInstrId $ execSE $ sequence_ initOuts
lastInstr = renderInstr krateSet fts lastInstrId lastInstrExp
lastInstrExp = mixingInstrExp globalEffect effects
scoSigOut' x = case x of
PlainSigOut _ _ -> scoSigOut x
_ -> []
dur = maybe 64000000 id $ totalDur as
renderTotalDur = ppTotalDur dur
firstInstrNote = alwayson firstInstrId dur
lastInstrNote = alwayson lastInstrId dur
alwayson instrId time = ppNote instrId 0 time []
krateSet = S.fromList $ csdKrate opt
globalEffect = csdEffect opt
midiAssignTable :: [Int] -> [SigOut] -> [MidiAssign]
midiAssignTable ids instrs = catMaybes $ zipWith mk ids instrs
where mk n instr = case sigOutContent $ instr of
Midi ty chn _ -> Just $ MidiAssign ty chn n
_ -> Nothing
renderTabs = ppMapTable ppTabDef
renderStringTable = ppMapTable ppStrset
mixingInstrExp :: ([[Sig]] -> SE [Sig]) -> [SE [Sig]] -> E
mixingInstrExp globalEffect effects = execSE $ outs' . fmap clip' =<< globalEffect =<< sequence effects
where clip' x = clip x 0 zeroDbfs
totalDur :: [SigOut] -> Maybe Double
totalDur as
| null as' = Nothing
| otherwise = Just $ maximum $ map eventEnd . scoSigOut =<< as'
where as' = filter isNotMidi $ map sigOutContent as
isNotMidi x = case x of
Midi _ _ _ -> False
_ -> True