packages feed

hsc3-0.19: Sound/SC3/UGen/Event.hs

-- | Event and Ctl systems for external control interfaces.
module Sound.SC3.UGen.Event where

import Sound.SC3.Common.Rate {- hsc3 -}
import Sound.SC3.UGen.Bindings.DB {- hsc3 -}
import Sound.SC3.UGen.Bindings.Composite {- hsc3 -}
import Sound.SC3.UGen.Type {- hsc3 -}
import Sound.SC3.UGen.UGen {- hsc3 -}

-- * Event

-- | (w/gate,x,y,z/force,orientation,radius-x,radius-y,pitch,pitch-x,pitch-y)
type REvent t = (t,t,t,t,t,t,t,t,t,t)

-- | Translate list to REvent.
rEvent_from_list :: Num t => [t] -> REvent t
rEvent_from_list l =
  case l of
    [w,x,y,z,o,rx,ry,p,px,py] -> (w,x,y,z,o,rx,ry,p,px,py)
    _ -> error "rEvent_from_list?"

{- | k0 = index of control bus zero for event system,
    stp = voice index incremennt,
      c = event channel or voice (zero indexed)
-}
rEventAddr :: UGen -> UGen -> UGen -> REvent UGen
rEventAddr k0 stp c =
  let u = in' 10 KR (k0 + (c * stp))
  in rEvent_from_list (mceChannels u)

-- | c0 = index of voice (channel) zero for event set, n = number of voices (channels)
rEventVoicerAddr :: UGen -> UGen -> UGen -> Int -> (Int -> REvent UGen -> UGen) -> UGen
rEventVoicerAddr k0 stp c0 n f = mce (map (\c -> f c (rEventAddr k0 stp (c0 + constant c))) [0 .. n - 1])

-- | 'rEventAddr' with 'control' inputs for /eventAddr/, /eventIncr/ and /eventZero/.
rEvent :: REvent UGen
rEvent = rEventAddr (control KR "eventAddr" 13000) (control KR "eventIncr" 10) (control KR "eventZero" 0)

-- | 'rEventVoicerAddr' with 'control' inputs for /eventAddr/, /eventIncr/ and /eventZero/.
rEventVoicer :: Int -> (Int -> REvent UGen -> UGen) -> UGen
rEventVoicer = rEventVoicerAddr (control KR "eventAddr" 13000) (control KR "eventIncr" 10) (control KR "eventZero" 0)

{- | Given /g/ and /p/ fields of an 'REvent' derive a 'gateReset' from g
and a trigger derived from monitoring /g/ and /p/ for changed values.
-}
rEventGateReset :: UGen -> UGen -> (UGen, UGen)
rEventGateReset g p = let tr = changed p 0.01 + changed g 0.01 in (gateReset g tr,tr)

-- * Ctl

-- | Sequence of 16 continous controller inputs in range (0-1).
type RCtl = (UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen)

-- | k0 = index of control bus zero for ctl system, c = ctl channel or voice (zero indexed)
rCtlAddr :: UGen -> UGen -> RCtl
rCtlAddr k0 c =
  let u = in' 16 KR (k0 + (c * 16))
  in case mceChannels u of
       [cc0,cc1,cc2,cc3,cc4,cc5,cc6,cc7,cc8,cc9,cc10,cc11,cc12,cc13,cc14,cc15] ->
         (cc0,cc1,cc2,cc3,cc4,cc5,cc6,cc7,cc8,cc9,cc10,cc11,cc12,cc13,cc14,cc15)
       _ -> error "rCtlAddr?"

-- | c0 = index of voice (channel) zero for ctl set, n = number of voices (channels)
rCtlVoicerAddr :: UGen -> UGen -> Int -> (Int -> RCtl -> UGen) -> UGen
rCtlVoicerAddr k0 c0 n f = mce (map (\c -> f c (rCtlAddr k0 (c0 + constant c))) [0 .. n - 1])

-- | 'rCtlAddr' with 'control' inputs for /CtlAddr/ and /CtlZero/.
rCtl :: RCtl
rCtl = rCtlAddr (control KR "CtlAddr" 11000) (control KR "CtlZero" 0)

-- | 'rCtlVoicerAddr' with 'control' inputs for /CtlAddr/ and /CtlZero/.
rCtlVoicer :: Int -> (Int -> RCtl -> UGen) -> UGen
rCtlVoicer = rCtlVoicerAddr (control KR "CtlAddr" 11000) (control KR "CtlZero" 0)

-- | First eight elements of RCtl.
type RCtl8 = (UGen,UGen,UGen,UGen,UGen,UGen,UGen,UGen)

-- | Select first eight elements of RCtl.
rCtl_to_rCtl8 :: RCtl -> RCtl8
rCtl_to_rCtl8 (cc0,cc1,cc2,cc3,cc4,cc5,cc6,cc7,_,_,_,_,_,_,_,_) = (cc0,cc1,cc2,cc3,cc4,cc5,cc6,cc7)

-- | 'rCtlVoicer' of 'rCtl_to_rCtl8'
rCtl8Voicer :: Int -> (Int -> RCtl8 -> UGen) -> UGen
rCtl8Voicer k0 f = rCtlVoicer k0 (\n c -> f n (rCtl_to_rCtl8 c))

-- * Names

{- | See SCClassLibrary/Common/Control/Spec:ControlSpec.initClass

"ControlSpec defines the range and curve of a control"
-}
sc3_control_spec :: Fractional t => [(String,(t,t,String))]
sc3_control_spec =
  [("amp",(0,1,"amp"))
  ,("beats",(0,20,"lin"))
  ,("bipolar",(-1,1,"lin"))
  ,("boostcut",(-20,20,"lin"))
  ,("db",(-180,0,"db"))
  ,("delay",(0.0001,1,"exp"))
  ,("detune",(-20,20,"lin"))
  ,("freq",(20,20000,"exp"))
  ,("lofreq",(0.1,100,"exp"))
  ,("midfreq",(25,4200,"exp"))
  ,("midi",(0,127,"lin"))
  ,("midinote",(0,127,"lin"))
  ,("midivelocity",(1,127,"lin"))
  ,("pan",(-1,1,"lin"))
  ,("phase",(0,6.28318,"lin"))
  ,("rate",(0.125,8,"exp"))
  ,("rq",(0.001,2,"exp"))
  ,("unipolar",(0,1,"lin"))
  ,("widefreq",(0.1,20000,"exp"))]

{- | See Kyma X Revealed, p.403

"The following EventValue names are associated with initial ranges
other than (0,1). EventValue names are not case-sensitive."

This list adds curve specifiers as strings.

> let x = Data.List.intersect (map fst sc3_control_spec) (map fst kyma_event_value_ranges)
> x == ["beats","boostcut","freq","rate"]
> let c z = let (p,q) = unzip z in let f i = filter (flip elem i . fst) in zip (f p sc3_control_spec) (f q kyma_event_value_ranges)
> c (zip x x)

> c [("lofreq","freqlow"),("midfreq","freqmid")]
> lookup "freqhigh" kyma_event_value_ranges
-}
kyma_event_value_ranges :: Fractional t => [(String,(t,t,String))]
kyma_event_value_ranges =
  [("angle",(-0.5,1.5,"lin"))
  ,("beats",(1,16,"lin"))
  ,("boostcut",(-12,12,"lin"))
  ,("bpm",(0,2000,"lin"))
  ,("centervalue",(-1,1,"lin"))
  ,("coef",(-1,1,"lin"))
  ,("cutoff",(0,10000,"exp"))
  ,("cycles",(0,100,"lin"))
  ,("dcoffset",(-1,1,"lin"))
  ,("direction",(-1,1,"lin"))
  ,("distance",(-2,2,"lin"))
  ,("fmntshift",(0.75,1.25,"lin"))
  ,("freq",(0,10000,"exp"))
  ,("freqhigh",(8000,24000,"exp")) -- sampleRate / 2
  ,("freqjitter",(0,1,"lin"))
  ,("freqlow",(0,1000,"exp"))
  ,("freqmid",(1000,8000,"exp"))
  ,("gain",(0,10,"amp"))
  ,("gaindb",(-128,128,"lin"))
  ,("interval",(-24,24,"lin"))
  ,("keynumber",(0,127,"lin"))
  ,("logfreq",(0,127,"lin"))
  ,("looplength",(-1,1,"lin"))
  ,("offset",(-1,1,"lin"))
  ,("onduration",(0,30,"lin"))
  ,("panner",(-0.5,1.5,"lin"))
  ,("pitch",(0,127,"lin"))
  ,("q",(0,10,"lin"))
  ,("radius",(-2,2,"lin"))
  ,("rate",(0,2,"lin"))
  ,("ratio",(0,100,"lin"))
  ,("scale",(-2,2,"lin"))
  ,("smallInterval",(0,12,"lin"))
  ,("steps",(1,128,"lin"))
  ,("swing",(0,0.5,"lin"))
  ,("threshdb",(-60,0,"lin"))
  ,("timeconstant",(0.0001,5,"lin"))
  ,("timeindex",(-1,1,"lin"))
  ,("tune",(-1,1,"lin"))
  ,("upinterval",(0,24,"lin"))]