{-# Language DeriveFunctor #-} module Csound.Typed.GlobalState.Elements( -- * Identifiers IdMap(..), saveId, newIdMapId, -- ** Gens GenMap, newGen, newGenId, nextGlobalGenCounter, newTabOfGens, WriteGenMap, newWriteGen, newWriteTab, -- Sf2 SfFluid(..), SfSpec(..), SfMap, newSf, sfVar, renderSf, -- ** Band-limited waveforms BandLimited(..), BandLimitedMap(..), BandLimitedId(..), saveBandLimited, renderBandLimited, readBandLimited, readHardSyncBandLimited, -- ** String arguments StringMap, newString, -- * Midi MidiType(..), Channel, MidiMap, MidiKey(..), saveMidiInstr, -- * Global variables Globals(..), newPersistentGlobalVar, newClearableGlobalVar, newPersistentGloabalArrVar, renderGlobals, bpmVarName, bpmVar, -- * Instruments Instrs(..), saveInstr, getInstrIds, -- newInstrId, saveInstrById, saveInstr, CacheName, makeCacheName, saveCachedInstr, getInstrIds, -- * Named instruments NamedInstrs(..), saveNamedInstr, -- * Src InstrBody, getIn, sendOut, sendChn, sendGlobal, chnPargId, Event(..), ChnRef(..), chnRefFromParg, chnRefAlloc, readChn, writeChn, chnUpdateUdo, subinstr, subinstr_, event_i, event, safeOut, autoOff, changed, -- * OSC listen ports OscListenPorts, getOscPortVar, -- * Macros inits MacrosInits, MacrosInit(..), initMacros, -- * Udo plugins UdoPlugin, addUdoPlugin, getUdoPluginNames, tabQueuePlugin, tabQueue2Plugin, zdfPlugin, solinaChorusPlugin, audaciouseqPlugin, adsr140Plugin, diodePlugin, korg35Plugin, zeroDelayConvolutionPlugin, pitchShifterDelayPlugin, analogDelayPlugin, distortionPlugin, envelopeFolollowerPlugin, flangerPlugin, freqShifterPlugin, loFiPlugin, panTremPlugin, monoTremPlugin, phaserPlugin, pitchShifterPlugin, reversePlugin, ringModulatorPlugin, stChorusPlugin, stereoPingPongDelayPlugin, tapeEchoPlugin, delay1kPlugin, liveRowPlugin, liveRowsPlugin, ambiRowPlugin, ambiRowMp3Plugin ) where import Data.List import Data.ByteString (ByteString) import Control.Monad.Trans.State.Strict import Control.Monad(zipWithM_) import Data.Default import qualified Data.Map.Strict as M import qualified Data.IntMap as IM import Csound.Dynamic.Types hiding (genId) import Csound.Dynamic.Build import Csound.Dynamic.Build.Numeric() import Csound.Typed.GlobalState.Opcodes import Data.Text (Text) import Data.Text qualified as Text -- tables of identifiers data IdMap a = IdMap { idMapContent :: M.Map a Int , idMapNewId :: Int } deriving (Eq, Ord) instance Default (IdMap a) where def = IdMap def 1 saveId :: Ord a => a -> State (IdMap a) Int saveId a = state $ \s -> case M.lookup a (idMapContent s) of Nothing -> let newId = idMapNewId s s1 = s{ idMapContent = M.insert a newId (idMapContent s) , idMapNewId = succ newId } in (newId, s1) Just n -> (n, s) newIdMapId :: State (IdMap a) Int newIdMapId = state $ \s -> let newId = idMapNewId s s1 = s { idMapNewId = succ newId } in (newId, s1) -- gens type GenMap = IdMap Gen newGen :: Gen -> State GenMap Int newGen = saveGenId newTabOfGens :: [Gen] -> State GenMap Int newTabOfGens = (saveGenId . intTab =<<) . mapM saveGenId where intTab ns = Gen (nextPowOfTwo $ length ns) (IntGenId (-2)) (fmap fromIntegral ns) Nothing nextPowOfTwo :: Int -> Int nextPowOfTwo n | frac == 0 = n | otherwise = 2 ^ ((integ :: Int) + 1) where (integ, frac) = (properFraction $ logBase 2 (fromIntegral n)) :: (Int, Double) saveGenId :: Ord a => a -> State (IdMap a) Int saveGenId a = state $ \s -> case M.lookup a (idMapContent s) of Nothing -> let newId = nextReadOnlyTableId $ idMapNewId s s1 = s{ idMapContent = M.insert a newId (idMapContent s) , idMapNewId = nextReadOnlyTableId newId } in (newId, s1) Just n -> (n, s) newGenId :: State GenMap Int newGenId = state $ \s -> let newId = idMapNewId s s1 = s { idMapNewId = nextReadOnlyTableId newId } in (newId, s1) -- writeable gens type WriteGenMap = [(Int, Gen)] newWriteGen :: Gen -> State WriteGenMap E newWriteGen = fmap int . saveWriteGenId newWriteTab :: Int -> State WriteGenMap E newWriteTab = newWriteGen . fromSize where fromSize n = Gen n (IntGenId 2) (replicate n 0) Nothing saveWriteGenId :: Gen -> State WriteGenMap Int saveWriteGenId a = state $ \s -> case s of [] -> (initId, [(initId, a)]) (i,_):_ -> let newId = nextWriteTableId i in (newId, (newId, a) : s) where initId = tableWriteStep tableWriteStep :: Int tableWriteStep = 10 nextReadOnlyTableId :: Int -> Int nextReadOnlyTableId x | y `mod` tableWriteStep == 0 = y + 1 | otherwise = y where y = x + 1 nextWriteTableId :: Int -> Int nextWriteTableId x = tableWriteStep + x -- strings type StringMap = IdMap Text newString :: Text -> State StringMap Prim newString = fmap PrimInt . saveId -- gen counter nextGlobalGenCounter :: State Int Int nextGlobalGenCounter = state $ \s -> (s, s + 1) -- sf data SfFluid = SfFluid { sfId :: Int , sfVars :: [Var] } data SfSpec = SfSpec { sfName :: Text , sfBank :: Int , sfProgram :: Int } deriving (Eq, Ord, Show) type SfMap = IdMap SfSpec newSf :: SfSpec -> State SfMap Int newSf = saveId sfVar :: Int -> E sfVar n = readOnlyVar (VarVerbatim Ir $ sfEngineName n) sfEngineName :: Int -> Text sfEngineName n = "gi_Sf_engine_" <> Text.pack (show n) sfInstrName :: Int -> Text sfInstrName n = "i_Sf_instr_" <> Text.pack (show n) renderSf :: Monad m => SfSpec -> Int -> DepT m () renderSf (SfSpec name bank prog) n = verbatim $ Text.unlines [ engineStr , loadStr , selectProgStr ] where engineStr = engineName <> " fluidEngine" loadStr = insName <> " fluidLoad \"" <> name <> "\", " <> engineName <> ", 1" selectProgStr = "fluidProgramSelect " <> engineName <> ", 1, " <> insName <> ", " <> Text.pack (show bank) <> ", " <> Text.pack (show prog) engineName = sfEngineName n insName = sfInstrName n -- band-limited waveforms (used with vco2init) data BandLimited = Saw | Pulse | Square | Triangle | IntegratedSaw | UserGen Gen deriving (Eq, Ord) data BandLimitedId = SimpleBandLimitedWave Int | UserBandLimitedWave Int deriving (Eq, Ord) bandLimitedIdToExpr :: BandLimitedId -> E bandLimitedIdToExpr x = case x of SimpleBandLimitedWave simpleId -> int simpleId UserBandLimitedWave userId -> noRate $ ReadVar $ bandLimitedVar userId bandLimitedVar :: Show a => a -> Var bandLimitedVar userId = Var GlobalVar Ir ("BandLim" <> Text.pack (show userId)) data BandLimitedMap = BandLimitedMap { simpleBandLimitedMap :: M.Map BandLimited BandLimitedId , vcoInitMap :: GenMap } deriving (Eq, Ord) instance Default BandLimitedMap where def = BandLimitedMap def def saveBandLimited :: BandLimited -> State BandLimitedMap BandLimitedId saveBandLimited x = case x of Saw -> simpleWave 1 0 IntegratedSaw -> simpleWave 2 1 Pulse -> simpleWave 4 2 Square -> simpleWave 8 3 Triangle -> simpleWave 16 4 UserGen gen -> userGen gen where simpleWave writeId readId = state $ \blMap -> if (M.member x (simpleBandLimitedMap blMap)) then (SimpleBandLimitedWave readId, blMap) else (SimpleBandLimitedWave readId, blMap { simpleBandLimitedMap = M.insert x (SimpleBandLimitedWave writeId) (simpleBandLimitedMap blMap) }) userGen gen = state $ \blMap -> let genMap = vcoInitMap blMap (newId, genMap1) = runState (saveId gen) genMap blMap1 = blMap { vcoInitMap = genMap1 } in (UserBandLimitedWave newId, blMap1) renderBandLimited :: Monad m => GenMap -> BandLimitedMap -> DepT m () renderBandLimited genMap blMap = if isEmptyBlMap blMap then return () else render (idMapNewId genMap) (M.toList $ idMapContent $ vcoInitMap blMap) (M.toList $ simpleBandLimitedMap blMap) where isEmptyBlMap m = (M.null $ simpleBandLimitedMap m) && (M.null $ idMapContent $ vcoInitMap m) render lastGenId gens vcos = do writeVar freeVcoVar $ int (lastGenId + length gens + 100) mapM_ (renderGen lastGenId) gens mapM_ renderVco vcos renderGen :: Monad m => Int -> (Gen, Int) -> DepT m () renderGen lastGenId (gen, genId) = do renderFtgen lastGenId (gen, genId) renderVcoGen genId renderVcoVarAssignment genId freeVcoVar = Var GlobalVar Ir "free_vco" ftVar n = Var GlobalVar Ir $ "vco_table_" <> Text.pack (show n) renderFtgen lastGenId (g, n) = writeVar (ftVar n) $ ftgen (int $ lastGenId + n) g renderVcoGen ftId = do ft <- readVar (ftVar ftId) free <- readVar freeVcoVar writeVar freeVcoVar $ vco2init [-ft, free, 1.05, -1, -1, ft] renderVcoVarAssignment n = writeVar (bandLimitedVar n) =<< (fmap negate $ readVar (ftVar n)) renderVco :: Monad m => (BandLimited, BandLimitedId) -> DepT m () renderVco (_bandLimited, blId) = case blId of SimpleBandLimitedWave waveId -> do free <- readVar freeVcoVar writeVar freeVcoVar $ vco2init [int waveId, free] UserBandLimitedWave _ -> return () readBandLimited :: Maybe E -> BandLimitedId -> E -> E readBandLimited mphase n cps = oscilikt 1 cps (vco2ft cps (bandLimitedIdToExpr n)) mphase readHardSyncBandLimited :: Maybe BandLimitedId -> Maybe E -> BandLimitedId -> E -> E -> E readHardSyncBandLimited msmoothShape mphase n slaveCps masterCps = smoothWave * readShape n phasorSlave slaveCps where (phasorMaster, syncMaster) = syncphasor masterCps 0 Nothing (phasorSlave, _syncSlave) = syncphasor slaveCps syncMaster mphase smoothWave = case msmoothShape of Nothing -> 1 Just shape -> readShape shape phasorMaster masterCps readShape shapeId phasor freq = tableikt phasor (vco2ft freq (bandLimitedIdToExpr shapeId)) ---------------------------------------------------------- -- Midi type Channel = Int data MidiType = Massign | Pgmassign (Maybe Int) deriving (Show, Eq, Ord) data MidiKey = MidiKey MidiType Channel deriving (Show, Eq, Ord) type MidiMap m = M.Map MidiKey (DepT m ()) saveMidiInstr :: Monad m => MidiType -> Channel -> DepT m () -> MidiMap m -> MidiMap m saveMidiInstr ty chn body = M.insertWith (flip (>>)) (MidiKey ty chn) body -- global variables data Globals = Globals { globalsNewId :: Int , globalsVars :: [AllocVar] } data AllocVar = AllocVar { _allocVarType :: GlobalVarType , _allocVar :: Var , _allocVarInit :: E } | AllocArrVar { _allocArrVar :: Var , _allocArrVarSizes :: [E] } data GlobalVarType = PersistentGlobalVar | ClearableGlobalVar deriving (Eq) instance Default Globals where def = Globals 0 [AllocVar PersistentGlobalVar bpmVar 110] bpmVar :: Var bpmVar = Var GlobalVar Kr bpmVarName bpmVarName :: Text bpmVarName = "gBpmVar" newGlobalVar :: GlobalVarType -> Rate -> E -> State Globals Var newGlobalVar ty rate initVal = state $ \s -> let newId = globalsNewId s var = Var GlobalVar rate (Text.cons 'g' (Text.pack $ show newId)) s1 = s { globalsNewId = succ newId , globalsVars = AllocVar ty var initVal : globalsVars s } in (var, s1) newPersistentGlobalVar :: Rate -> E -> State Globals Var newPersistentGlobalVar = newGlobalVar PersistentGlobalVar newClearableGlobalVar :: Rate -> E -> State Globals Var newClearableGlobalVar = newGlobalVar ClearableGlobalVar newPersistentGloabalArrVar :: Rate -> [E] -> State Globals Var newPersistentGloabalArrVar rate sizes = state $ \s -> let newId = globalsNewId s var = Var GlobalVar rate (Text.cons 'g' (Text.pack $ show newId)) s1 = s { globalsNewId = succ newId , globalsVars = AllocArrVar var sizes : globalsVars s } in (var, s1) renderGlobals :: Monad m => Globals -> (DepT m (), DepT m ()) renderGlobals a = (initAll, clear) where initAll = mapM_ initAlloc gs clear = mapM_ clearAlloc clearable clearable = filter isClearable gs gs = globalsVars a initAlloc x = case x of AllocVar _ var initProc -> initVar var initProc AllocArrVar var sizes -> initArr var sizes clearAlloc x = case x of AllocVar _ var initProc -> writeVar var initProc AllocArrVar _ _ -> return () isClearable x = case x of AllocVar ty _ _ -> ty == ClearableGlobalVar _ -> False ----------------------------------------------------------------- -- instrs data Instrs = Instrs { instrsCache :: M.Map ByteString InstrId , instrsNewId :: Int , instrsContent :: [(InstrId, InstrBody)] } instance Default Instrs where def = Instrs M.empty 18 [] getInstrIds :: Instrs -> [InstrId] getInstrIds = fmap fst . instrsContent ----------------------------------------------------------------- -- saveInstr :: InstrBody -> State Instrs InstrId saveInstr body = state $ \s -> let h = hashE body in case M.lookup h $ instrsCache s of Just n -> (n, s) Nothing -> let newId = instrsNewId s s1 = s { instrsCache = M.insert h (intInstrId newId) $ instrsCache s , instrsNewId = succ newId , instrsContent = (intInstrId newId, body) : instrsContent s } in (intInstrId newId, s1) ----------------------------------------------------------------- -- named instrs newtype NamedInstrs = NamedInstrs { unNamedInstrs :: [(Text, InstrBody)] } instance Default NamedInstrs where def = NamedInstrs [] saveNamedInstr :: Text -> InstrBody -> State NamedInstrs () saveNamedInstr name body = state $ \(NamedInstrs xs) -> ((), NamedInstrs $ (name, body) : xs) ----------------------------------------------------------------- -- sound sources getIn :: Monad m => Int -> DepT m [E] getIn arity | arity == 0 = return [] | otherwise = ($ arity ) $ mdepT $ mopcs "inch" (replicate arity Ar, replicate arity Kr) (fmap int [1 .. arity]) sendOut :: Monad m => Int -> [E] -> DepT m () sendOut arity sigs | arity == 0 = return () | otherwise = do vars <- newLocalVars (replicate arity Ar) (return $ replicate arity 0) zipWithM_ writeVar vars sigs vals <- mapM readVar vars depT_ $ opcsNoInlineArgs name [(Xr, replicate arity Ar)] vals where name | arity == 1 = "out" | arity == 2 = "outs" | arity == 4 = "outq" | arity == 6 = "outh" | arity == 8 = "outo" | arity == 16 = "outx" | arity == 32 = "out32" | otherwise = "outc" sendGlobal :: Monad m => Int -> [E] -> State Globals ([E], DepT m ()) sendGlobal arityOuts sigs = do vars <- mapM (uncurry newClearableGlobalVar) $ replicate arityOuts (Ar, 0) return (fmap readOnlyVar vars, zipWithM_ (appendVarBy (+)) vars sigs) sendChn :: Monad m => Int -> Int -> [E] -> DepT m () sendChn arityIns arityOuts sigs = writeChn (chnRefFromParg (chnPargId arityIns) arityOuts) sigs chnPargId :: Int -> Int chnPargId arityIns = 4 + arityIns -- scaleVolumeFactor :: E -> E -- scaleVolumeFactor = (setRate Ir (C.midiVolumeFactor (pn 1)) * ) -- guis -------------------------------------------------------- -- Osc listeners newtype OscListenPorts = OscListenPorts (IM.IntMap Var) instance Default OscListenPorts where def = OscListenPorts IM.empty getOscPortVar :: Int -> State (OscListenPorts, Globals) Var getOscPortVar portId = state $ \st@(OscListenPorts m, globals) -> case IM.lookup portId m of Just a -> (a, st) Nothing -> onNothing m globals where onNothing m globals = (var, (OscListenPorts m1, newGlobals)) where (var, newGlobals) = runState (allocOscPortVar portId) globals m1 = IM.insert portId var m allocOscPortVar :: Int -> State Globals Var allocOscPortVar oscPort = newGlobalVar PersistentGlobalVar Ir $ oscInit (fromIntegral oscPort) ---------------------------------------------------------- -- macros arguments type MacrosInits = M.Map Text MacrosInit data MacrosInit = MacrosInitDouble { macrosInitName :: Text, macrosInitValueDouble :: Double } | MacrosInitString { macrosInitName :: Text, macrosInitValueString :: Text } | MacrosInitInt { macrosInitName :: Text, macrosInitValueInt :: Int } deriving (Show, Eq, Ord) initMacros :: MacrosInit -> State MacrosInits () initMacros macrosInit = modify $ \xs -> M.insert (macrosInitName macrosInit) macrosInit xs -------------------------------------------------------- -- Udo plugins newtype UdoPlugin = UdoPlugin { unUdoPlugin :: Text } addUdoPlugin :: UdoPlugin -> State [UdoPlugin] () addUdoPlugin a = modify (a :) getUdoPluginNames :: [UdoPlugin] -> [Text] getUdoPluginNames xs = nub (fmap unUdoPlugin xs) -- tabQueue tabQueuePlugin = UdoPlugin "tabQueue" tabQueue2Plugin = UdoPlugin "tabQueue2" ---------------------------------------------------------- -- Steven Yi wonderful UDOs zdfPlugin, solinaChorusPlugin, audaciouseqPlugin, adsr140Plugin, diodePlugin, korg35Plugin, zeroDelayConvolutionPlugin, analogDelayPlugin, distortionPlugin, envelopeFolollowerPlugin, flangerPlugin, freqShifterPlugin, loFiPlugin, panTremPlugin, monoTremPlugin, phaserPlugin, pitchShifterPlugin, pitchShifterDelayPlugin, reversePlugin, ringModulatorPlugin, stChorusPlugin, stereoPingPongDelayPlugin, tapeEchoPlugin, delay1kPlugin, ambiRowPlugin, ambiRowMp3Plugin, liveRowPlugin, liveRowsPlugin, tabQueuePlugin, tabQueue2Plugin :: UdoPlugin zdfPlugin = UdoPlugin "zdf" -- Zero delay filters solinaChorusPlugin = UdoPlugin "solina_chorus" -- solina chorus audaciouseqPlugin = UdoPlugin "audaciouseq" -- audacious 10 band EQ adsr140Plugin = UdoPlugin "adsr140" -- adsr with retriggering diodePlugin = UdoPlugin "diode" -- diode ladder filter korg35Plugin = UdoPlugin "korg35" -- korg 35 filter zeroDelayConvolutionPlugin = UdoPlugin "zero-delay-convolution" -- zero delay convolutio by Victor Lazzarini pitchShifterDelayPlugin = UdoPlugin "PitchShifterDelay" -- pitch shifter delay analogDelayPlugin = UdoPlugin "MultiFX/AnalogDelay" distortionPlugin = UdoPlugin "MultiFX/Distortion" envelopeFolollowerPlugin = UdoPlugin "MultiFX/EnvelopeFollower" flangerPlugin = UdoPlugin "MultiFX/Flanger" freqShifterPlugin = UdoPlugin "MultiFX/FreqShifter" loFiPlugin = UdoPlugin "MultiFX/LoFi" panTremPlugin = UdoPlugin "MultiFX/PanTrem" monoTremPlugin = UdoPlugin "MultiFX/MonoTrem" phaserPlugin = UdoPlugin "MultiFX/Phaser" pitchShifterPlugin = UdoPlugin "MultiFX/PitchShifter" reversePlugin = UdoPlugin "MultiFX/Reverse" ringModulatorPlugin = UdoPlugin "MultiFX/RingModulator" stChorusPlugin = UdoPlugin "MultiFX/StChorus" stereoPingPongDelayPlugin = UdoPlugin "MultiFX/StereoPingPongDelay" tapeEchoPlugin = UdoPlugin "MultiFX/TapeEcho" delay1kPlugin = UdoPlugin "Utility/Delay1k" liveRowPlugin = UdoPlugin "LiveRow" -- live like trigger, mono liveRowsPlugin = UdoPlugin "LiveRows" -- stereo ambiRowPlugin = UdoPlugin "AmbiRow" -- ambi trigger, wav ambiRowMp3Plugin = UdoPlugin "AmbiRowMp3" -- mp3