prev (24) slide 25 / 33 next (26)
track lang
data Val =
-- | Goes in a pitch track val field. Literal: @*5a@
VNote Pitch.Note
...
class Typecheck a where
type_check :: Val → Maybe a
type_name :: a → String
instance Typecheck Pitch.Note where
type_check (VNote a) = Just a
type_check _ = Nothing
type_name _ = "note"
...
call2 :: (Typecheck a, Typecheck b) =>
[Val] → (Arg a, Arg b) → (a → b → result) → Either TypeError result
call2 vals (arg0, arg1) f = do
(val0, val1) <- extract2 vals (arg0, arg1)
return $ f val0 val1
extract2 :: (Typecheck a, Typecheck b) =>
[Val] → (Arg a, Arg b) → Either TypeError (a, b)
extract2 vals (sig0, sig1) = do
arg0 : arg1 : _ <- check_args vals [arg_opt sig0, arg_opt sig1]
liftM2 (,) (extract_arg 0 sig0 arg0) (extract_arg 1 sig1 arg1)
c_echo :: Derive.Call
c_echo args events = TrackLang.call3 args
( optional "delay" (signal 1 "echo-delay")
, optional "feedback" (signal 0.4 "echo-feedback")
, optional "times" (signal 1 "echo-times")) $ \delay feedback times →
Call.map_any events () $ Call.with_signals [delay, feedback, times] $
\[delay, feedback, times] (_, _, event, _) →
return ((), echo (Signal.y_to_x delay) feedback (floor times) event)
- If arg is not optional, type is inferred from haskell use.
Haskell people love to write interpreters so much, there must be a
better way.
- Infer types for block calls, and substitute variables.
- Put derivers around tracks like
>inst +attr | echo 1 %feedback,.2
- Interesting choices with echo: if it operates on a DeriveT, it will obey
behavioural abstraction. If it operates on [Score.Event], 'times' can
be a non-constant signal. Or, instead of emitting [Score.Event],
emit [DeriveT].
If feedback is constant, it can remain lazy, otherwise it must process
all events at once.
prev (24) slide 25 / 33 next (26)