{-# Language ScopedTypeVariables #-} module Csound.Typed.Control.Api( trigByName, trigByName_, trigByNameMidi, trigByNameMidi_, namedMonoMsg ) where import Data.Boolean import qualified Csound.Dynamic as D import Csound.Typed.Types import Csound.Typed.Control.Ref import Csound.Typed.GlobalState import Csound.Typed.GlobalState.Opcodes(eventi, Event(..), downsamp) import Csound.Typed.InnerOpcodes import Csound.Typed.Plugins.TabQueue -- | Creates an instrument that can be triggered by name with Csound API. -- The arguments are determined from the structure of the input for the instrument. -- -- With Csound API we can send messages -- -- > i "name" time duration arg1 arg2 arg3 trigByName_ :: Arg a => String -> (a -> SE ()) -> SE () trigByName_ name instr = geToSe $ saveNamedInstr name =<< (execSE $ instr toArg) -- | Creates an instrument that can be triggered by name with Csound API. -- The arguments are determined from the structure of the input for the instrument. -- If we have a tuple of arguments: @(D, D, Tab)@ -- The would be rendered to instrument arguments that strts from @p4@. -- @p1@ is the name of teh instrument, @p2@ is the start time of the note, -- @p3@ is the duration of the note. Then @p4@ and @p5@ are going to be doubles and @p6@ -- is an integer that denotes a functional table. trigByName :: (Arg a, Sigs b) => String -> (a -> SE b) -> SE b trigByName name instr = do ref <- newClearableGlobalRef 0 trigByName_ name (go ref) readRef ref where go ref x = mixRef ref =<< instr x -- | It behaves just like the function @trigByNameMidi@. Only it doesn't produce an audio -- signal. It performs some procedure on note on and stops doing the precedure on note off. trigByNameMidi_ :: forall a . Arg a => String -> ((D, D, a) -> SE ()) -> SE () trigByNameMidi_ name instr = do instrId <- geToSe $ saveInstr (instr toArg) trigByName_ name (go instrId) where go :: D.InstrId -> (D, D, D, a) -> SE () go instrId (noteFlag, pch, vol, other) = fromDep_ $ hideGEinDep $ do pchExpr <- toGE pch let instrIdExpr = D.instrIdE instrId + pchExpr / 1000 noteFlagExpr <- toGE noteFlag args <- fromTuple (pch, vol, other) return $ do D.when1 D.Ir (noteFlagExpr ==* 1) $ do eventi (Event instrIdExpr 0 (-1) args) D.when1 D.Ir (noteFlagExpr ==* 0) $ do eventi (Event (negate instrIdExpr) 0 0 args) turnoff -- | Creates an instrument that can be triggered by name with Csound API. -- -- It's intended to be used like a midi instrument. It simulates a simplified midi protocol. -- We can trigger notes: -- -- > i "givenName" delay duration 1 pitchKey volumeKey auxParams -- note on -- > i "givenName" delay duration 0 pitchKey volumeKey auxParams -- note off -- -- The arguments are -- -- > trigByNameMidi name instrument -- -- The instrument takes a triplet of @(pitchKey, volumeKey, auxilliaryTuple)@. -- The order does matter. Please don't pass the @volumeKey@ as the first argument. -- The instrument expects the pitch key to be a first argument. -- Under the hood -- it creates held notes that are indexed by pitch. If you know the Csound it creates -- the notes with indexes: -- -- > i 18.pitchKey -- -- Here the 18 is some generated integer index. And then on receiving a note a note off message for the specific key the -- Csound procedure invokes: -- -- > turnoff 18.pitchKey trigByNameMidi :: (Arg a, Sigs b) => String -> ((D, D, a) -> SE b) -> SE b trigByNameMidi name instr = do ref <- newClearableGlobalRef 0 trigByNameMidi_ name (go ref) readRef ref where go ref x = mixRef ref =<< instr x namedMonoMsg :: String -> SE MonoArg namedMonoMsg name = do refPch <- newGlobalRef 0 refVol <- newGlobalRef 0 tab <- newGlobalTab 24 let onFlag = tabQueue2_hasElements tab trigByNameMidiCbk name (onNote tab) (offNote tab) when1 onFlag $ do let (pch, vol) = tabQueue2_readLastElement tab writeRef refPch pch writeRef refVol vol when1 (notB onFlag) $ do writeRef refVol 0 pchKey <- readRef refPch volKey <- readRef refVol let kgate = ifB onFlag 1 0 kamp = downsamp' volKey kcps = downsamp' pchKey trig = changed [kamp, kcps] return $ MonoArg kamp kcps kgate trig where onNote = tabQueue2_append offNote tab (pch, _vol) = tabQueue2_delete tab pch trigByNameMidiCbk :: String -> ((D, D) -> SE ()) -> ((D, D) -> SE ()) -> SE () trigByNameMidiCbk name noteOn noteOff = trigByName_ name go where go :: (D, D, D) -> SE () go (noteFlag, pch, vol) = do whenD1 (noteFlag ==* 1) $ noteOn (pch, vol) whenD1 (noteFlag ==* 0) $ noteOff (pch, vol) SE turnoff downsamp' :: Sig -> Sig downsamp' a = fromGE $ do a' <- toGE a return $ downsamp a'