hsc3-0.21: Sound/Sc3/Ugen/Ugen.hs
-- | Unit generator (Ugen) type and instances.
module Sound.Sc3.Ugen.Ugen where
import Data.Bits {- base -}
import qualified Data.Fixed as Fixed {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}
import qualified System.Random as Random {- random -}
import qualified Sound.Sc3.Common.Math as Math
import Sound.Sc3.Common.Math.Operator
import Sound.Sc3.Common.Mce
import Sound.Sc3.Common.Rate
import Sound.Sc3.Ugen.Brackets
import Sound.Sc3.Ugen.Constant
import Sound.Sc3.Ugen.Control
import Sound.Sc3.Ugen.Label
import Sound.Sc3.Ugen.Mrg
import Sound.Sc3.Ugen.Primitive
import Sound.Sc3.Ugen.Proxy
-- * Basic types
{- | Sc3 samples are 32-bit 'Float'.
hsc3 uses 64-bit 'Double'.
If 'Ugen' values are used more generally (ie. see hsc3-forth) 'Float' may be too imprecise, i.e. for representing time stamps.
-}
type Sample = Double
-- | Union type of Unit Generator forms.
data Ugen
= Constant_U Constant
| Control_U Control
| Label_U Label
| Primitive_U (Primitive Ugen)
| Proxy_U (Proxy Ugen)
| Mce_U (Mce Ugen)
| Mrg_U (Mrg Ugen)
deriving (Eq, Read, Show)
-- * Name
{- | Lookup operator name for operator Ugens, else Ugen name.
>>> map (\k -> ugen_user_name "BinaryOpUGen" (Special k)) [0, 2, 4, 9, 12, 17, 25]
["+","*","/",">","Min","Lcm","**"]
-}
ugen_user_name :: String -> Special -> String
ugen_user_name nm (Special n) = fromMaybe nm (ugen_operator_name nm n)
-- * Instances
instance EqE Ugen where
equal_to = mkBinaryOperator OpEq Math.sc3_eq
not_equal_to = mkBinaryOperator OpNe Math.sc3_neq
instance OrdE Ugen where
less_than = mkBinaryOperator OpLt Math.sc3_lt
less_than_or_equal_to = mkBinaryOperator OpLe Math.sc3_lte
greater_than = mkBinaryOperator OpGt Math.sc3_gt
greater_than_or_equal_to = mkBinaryOperator OpGe Math.sc3_gte
-- | 'Ugen' form or 'Math.sc3_round_to'.
roundTo :: Ugen -> Ugen -> Ugen
roundTo = mkBinaryOperator OpRoundTo Math.sc3_round_to
instance RealFracE Ugen where
properFractionE = error "Ugen.properFractionE"
truncateE = error "Ugen.truncateE"
roundE i = roundTo i 1
ceilingE = mkUnaryOperator OpCeil ceilingE
floorE = mkUnaryOperator OpFloor floorE
instance UnaryOp Ugen where
ampDb = mkUnaryOperator OpAmpDb ampDb
asFloat = mkUnaryOperator OpAsFloat asFloat
asInt = mkUnaryOperator OpAsInt asInt
cpsMidi = mkUnaryOperator OpCpsMidi cpsMidi
cpsOct = mkUnaryOperator OpCpsOct cpsOct
cubed = mkUnaryOperator OpCubed cubed
dbAmp = mkUnaryOperator OpDbAmp dbAmp
distort = mkUnaryOperator OpDistort distort
frac = mkUnaryOperator OpFrac frac
isNil = mkUnaryOperator OpIsNil isNil
log10 = mkUnaryOperator OpLog10 log10
log2 = mkUnaryOperator OpLog2 log2
midiCps = mkUnaryOperator OpMidiCps midiCps
midiRatio = mkUnaryOperator OpMidiRatio midiRatio
notE = mkUnaryOperator OpNot notE
notNil = mkUnaryOperator OpNotNil notNil
octCps = mkUnaryOperator OpOctCps octCps
ramp_ = mkUnaryOperator OpRamp_ ramp_
ratioMidi = mkUnaryOperator OpRatioMidi ratioMidi
softClip = mkUnaryOperator OpSoftClip softClip
squared = mkUnaryOperator OpSquared squared
instance BinaryOp Ugen where
iDiv = mkBinaryOperator OpIdiv iDiv
modE = mkBinaryOperator OpMod Fixed.mod'
lcmE = mkBinaryOperator OpLcm lcmE
gcdE = mkBinaryOperator OpGcd gcdE
roundUp = mkBinaryOperator OpRoundUp roundUp
trunc = mkBinaryOperator OpTrunc trunc
atan2E = mkBinaryOperator OpAtan2 atan2E
hypot = mkBinaryOperator OpHypot hypot
hypotx = mkBinaryOperator OpHypotx hypotx
fill = mkBinaryOperator OpFill fill
ring1 = mkBinaryOperator OpRing1 ring1
ring2 = mkBinaryOperator OpRing2 ring2
ring3 = mkBinaryOperator OpRing3 ring3
ring4 = mkBinaryOperator OpRing4 ring4
difSqr = mkBinaryOperator OpDifSqr difSqr
sumSqr = mkBinaryOperator OpSumSqr sumSqr
sqrSum = mkBinaryOperator OpSqrSum sqrSum
sqrDif = mkBinaryOperator OpSqrDif sqrDif
absDif = mkBinaryOperator OpAbsDif absDif
thresh = mkBinaryOperator OpThresh thresh
amClip = mkBinaryOperator OpAmClip amClip
scaleNeg = mkBinaryOperator OpScaleNeg scaleNeg
clip2 = mkBinaryOperator OpClip2 clip2
excess = mkBinaryOperator OpExcess excess
fold2 = mkBinaryOperator OpFold2 fold2
wrap2 = mkBinaryOperator OpWrap2 wrap2
firstArg = mkBinaryOperator OpFirstArg firstArg
randRange = mkBinaryOperator OpRandRange randRange
exprandRange = mkBinaryOperator OpExpRandRange exprandRange
-- instance MulAdd Ugen where mul_add = mulAdd
-- * Parser
-- | 'constant' of 'parse_double'.
parse_constant :: String -> Maybe Ugen
parse_constant = fmap constant . Math.parse_double
-- * Accessors
-- | See into 'Constant_U'.
un_constant :: Ugen -> Maybe Constant
un_constant u =
case u of
Constant_U c -> Just c
_ -> Nothing
-- | Value of 'Constant_U' 'Constant'.
u_constant :: Ugen -> Maybe Sample
u_constant = fmap constantValue . un_constant
-- | Erroring variant.
u_constant_err :: Ugen -> Sample
u_constant_err = fromMaybe (error "u_constant") . u_constant
-- * Mrg
-- | Multiple root graph constructor.
mrg :: [Ugen] -> Ugen
mrg u =
case u of
[] -> error "mrg: []"
[x] -> x
(x : xs) -> Mrg_U (Mrg x (mrg xs))
-- | See into 'Mrg_U', follows leftmost rule until arriving at non-Mrg node.
mrg_leftmost :: Ugen -> Ugen
mrg_leftmost u =
case u of
Mrg_U m -> mrg_leftmost (mrgLeft m)
_ -> u
-- * Predicates
-- | Constant node predicate.
isConstant :: Ugen -> Bool
isConstant = isJust . un_constant
-- | True if input is a sink 'Ugen', ie. has no outputs. Sees into Mrg.
isSink :: Ugen -> Bool
isSink u =
case mrg_leftmost u of
Primitive_U p -> null (ugenOutputs p)
Mce_U m -> all isSink (mce_to_list m)
_ -> False
-- | See into 'Proxy_U'.
un_proxy :: Ugen -> Maybe (Proxy Ugen)
un_proxy u =
case u of
Proxy_U p -> Just p
_ -> Nothing
-- | Is 'Ugen' a 'Proxy'?
isProxy :: Ugen -> Bool
isProxy = isJust . un_proxy
-- | Get Primitive from Ugen if Ugen is a Primitive.
ugenPrimitive :: Ugen -> Maybe (Primitive Ugen)
ugenPrimitive u =
case u of
Primitive_U p -> Just p
_ -> Nothing
-- | Is 'Ugen' a 'Primitive'?
isPrimitive :: Ugen -> Bool
isPrimitive = isJust . ugenPrimitive
-- * Mce
-- | Multiple channel expansion node constructor.
mce :: [Ugen] -> Ugen
mce xs =
case xs of
[] -> error "mce: []"
[x] -> Mce_U (Mce_Scalar x)
_ -> Mce_U (mce_from_list xs)
-- | Type specified 'mce_to_list'.
mceProxies :: Mce Ugen -> [Ugen]
mceProxies = mce_to_list
-- | Multiple channel expansion node ('Mce_U') predicate. Sees into Mrg.
isMce :: Ugen -> Bool
isMce u =
case mrg_leftmost u of
Mce_U _ -> True
_ -> False
{- | Output channels of Ugen as a list.
If required, preserves the RHS of an Mrg node in channel 0.
See also: mceChannel
-}
mceChannels :: Ugen -> [Ugen]
mceChannels u =
case u of
Mce_U m -> mce_to_list m
Mrg_U (Mrg x y) ->
case mceChannels x of
r : rs -> Mrg_U (Mrg r y) : rs
_ -> error "mceChannels"
_ -> [u]
-- | Number of channels to expand to. This function sees into Mrg, and is defined only for Mce nodes.
mceDegree :: Ugen -> Maybe Int
mceDegree u =
case mrg_leftmost u of
Mce_U m -> Just (length (mceProxies m))
_ -> Nothing
-- | Erroring variant.
mceDegree_err :: Ugen -> Int
mceDegree_err = fromMaybe (error "mceDegree: not mce") . mceDegree
-- | Extend Ugen to specified degree. Follows "leftmost" rule for Mrg nodes.
mceExtend :: Int -> Ugen -> [Ugen]
mceExtend n u =
case u of
Mce_U m -> mceProxies (mce_extend n m)
Mrg_U (Mrg x y) ->
case mceExtend n x of
r : rs -> Mrg_U (Mrg r y) : rs
_ -> error "mceExtend"
_ -> replicate n u
-- | Is Mce required, ie. are any input values Mce?
mceRequired :: [Ugen] -> Bool
mceRequired = any isMce
{- | Apply Mce transform to a list of inputs.
The transform extends each input so all are of equal length, and then transposes the matrix.
>>> mceInputTransform [mce [1, 2],mce [3, 4]] == Just [[1,3],[2,4]]
True
>>> mceInputTransform [mce [1, 2],mce [3, 4], mce [5, 6, 7]] == Just [[1,3,5],[2,4,6],[1,3,7]]
True
>>> mceInputTransform [mce [mce [1, 2], mce [3, 4]], mce [5, 6]] == Just [[mce [1, 2],5],[mce [3, 4],6]]
True
-}
mceInputTransform :: [Ugen] -> Maybe [[Ugen]]
mceInputTransform i =
if mceRequired i
then
let n = maximum (map mceDegree_err (filter isMce i))
in Just (transpose (map (mceExtend n) i))
else Nothing
-- | Build a Ugen after Mce transformation of inputs.
mceBuild :: ([Ugen] -> Ugen) -> [Ugen] -> Ugen
mceBuild f i =
case mceInputTransform i of
Nothing -> f i
Just i' -> let xs = map (mceBuild f) i' in Mce_U (mce_from_list xs)
{- | True if Mce is an immediate proxy for a multiple-out Primitive.
This is useful when disassembling graphs, ie. ugen_graph_forth_pp at hsc3-db.
It's also useful when editing a Primitive after it is constructed, as in bracketUgen.
-}
mce_is_direct_proxy :: Mce Ugen -> Bool
mce_is_direct_proxy m =
case m of
Mce_Scalar _ -> False
Mce_Vector _ ->
let p = map un_proxy (mce_to_list m)
p' = catMaybes p
in all isJust p
&& length (nub (map proxySource p')) == 1
&& map proxyIndex p' `isPrefixOf` [0 ..]
-- * Bracketed
{- | Attach Brackets (initialisation and cleanup message sequences) to Ugen.
For simplicity and clarity, brackets can only be attached to Primitive, Constant and Control nodes.
This will look into the direct (immediate) proxies of a Primitive.
-}
bracketUgen :: Ugen -> Brackets -> Ugen
bracketUgen u (pre, post) =
let err = error "bracketUgen: only Constants or Primitive Ugens or immediate proxies may have brackets"
rw_proxy pxy =
case pxy of
Proxy_U (Proxy p pix) ->
let (lhs, rhs) = primitiveBrackets p in Proxy_U (Proxy (p {primitiveBrackets = (lhs ++ pre, rhs ++ post)}) pix)
_ -> err
in case u of
Constant_U c -> let (lhs, rhs) = constantBrackets c in Constant_U (c {constantBrackets = (lhs ++ pre, rhs ++ post)})
Control_U c -> let (lhs, rhs) = controlBrackets c in Control_U (c {controlBrackets = (lhs ++ pre, rhs ++ post)})
Primitive_U p -> let (lhs, rhs) = primitiveBrackets p in Primitive_U (p {primitiveBrackets = (lhs ++ pre, rhs ++ post)})
Mce_U m ->
if mce_is_direct_proxy m
then Mce_U (mce_map rw_proxy m)
else err
_ -> err
-- | Retrieve Brackets from Ugen.
ugenBrackets :: Ugen -> Brackets
ugenBrackets u =
case u of
Constant_U c -> constantBrackets c
Control_U c -> controlBrackets c
Primitive_U p -> primitiveBrackets p
_ -> emptyBrackets
-- * Validators
-- | Ensure input 'Ugen' is valid, ie. not a sink.
checkInput :: Ugen -> Ugen
checkInput u =
if isSink u
then error ("checkInput: " ++ show u)
else u
-- * Constructors
-- | Constant value node constructor.
constant :: Real n => n -> Ugen
constant = Constant_U . flip Constant emptyBrackets . realToFrac
-- | Type specialised 'constant'.
int_to_ugen :: Int -> Ugen
int_to_ugen = constant
-- | Type specialised 'constant'.
float_to_ugen :: Float -> Ugen
float_to_ugen = constant
-- | Type specialised 'constant'.
double_to_ugen :: Double -> Ugen
double_to_ugen = constant
-- | Unit generator proxy node constructor.
proxy :: Ugen -> Int -> Ugen
proxy u n =
case u of
Primitive_U p -> Proxy_U (Proxy p n)
_ -> error "proxy: not primitive?"
-- | Determine the rate of a Ugen.
rateOf :: Ugen -> Rate
rateOf u =
case u of
Constant_U _ -> InitialisationRate
Control_U c -> controlOperatingRate c
Label_U _ -> InitialisationRate
Primitive_U p -> ugenRate p
Proxy_U p -> ugenRate (proxySource p)
Mce_U _ -> maximum (map rateOf (mceChannels u))
Mrg_U m -> rateOf (mrgLeft m)
-- | Apply proxy transformation if required.
proxify :: Ugen -> Ugen
proxify u =
case u of
Mce_U m -> mce (map proxify (mce_to_list m))
Mrg_U m -> mrg [proxify (mrgLeft m), mrgRight m]
Primitive_U p ->
let o = ugenOutputs p
in case o of
_ : _ : _ -> mce (map (proxy u) [0 .. length o - 1])
_ -> u
Constant_U _ -> u
_ -> error "proxify: illegal ugen"
{- | Filters with DemandRate inputs run at ControlRate.
This is a little unfortunate, it'd be nicer if the rate in this circumstance could be given.
-}
mk_ugen_select_rate :: String -> [Ugen] -> [Rate] -> Either Rate [Int] -> Rate
mk_ugen_select_rate nm h rs r =
let at_note note list index = if index < 0 || index >= length list then error note else list !! index -- hugs...
is_right e = case e of Right _ -> True; _ -> False -- hugs...
r' = either id (maximum . map (rateOf . at_note ("mkUgen: " ++ nm) h)) r
in if is_right r && r' == DemandRate && DemandRate `notElem` rs
then if ControlRate `elem` rs then ControlRate else error "mkUgen: DemandRate input to non-ControlRate filter"
else
if r' `elem` rs || r' == DemandRate
then r'
else error ("mkUgen: rate restricted: " ++ show (r, r', rs, nm))
{- | Construct proxied and multiple channel expanded Ugen.
cf = constant function, rs = rate set, r = rate, nm = name, i = inputs, i_mce = list of Mce inputs, o = outputs.
-}
mkUgen ::
Maybe ([Sample] -> Sample) ->
[Rate] ->
Either Rate [Int] ->
String ->
[Ugen] ->
Maybe [Ugen] ->
Int ->
Special ->
UgenId ->
Ugen
mkUgen cf rs r nm i i_mce o s z =
let i' = maybe i ((i ++) . concatMap mceChannels) i_mce
f h =
let r' = mk_ugen_select_rate nm h rs r
o' = replicate o r'
u = Primitive_U (Primitive r' nm h o' s z emptyBrackets)
in case cf of
Just cf' ->
if all isConstant h
then constant (cf' (mapMaybe u_constant h))
else u
Nothing -> u
in proxify (mceBuild f (map checkInput i'))
-- * Operators
-- | Operator Ugen constructor.
mkOperator :: ([Sample] -> Sample) -> String -> [Ugen] -> Int -> Ugen
mkOperator f c i s =
let ix = [0 .. length i - 1]
in mkUgen (Just f) all_rates (Right ix) c i Nothing 1 (Special s) NoId
-- | Unary math constructor.
mkUnaryOperator :: Sc3_Unary_Op -> (Sample -> Sample) -> Ugen -> Ugen
mkUnaryOperator i f a =
let g [x] = f x
g _ = error "mkUnaryOperator: non unary input"
in mkOperator g "UnaryOpUGen" [a] (fromEnum i)
{- | Binary math constructor with constant optimisation.
>>> constant 2 * constant 3 == constant 6
True
>>> let o = mkUgen Nothing [AudioRate] (Left AudioRate) "SinOsc" [constant 440, constant 0] Nothing 1 (Special 0) (Uid 0)
>>> o * 1 == o && 1 * o == o && o * 2 /= o
True
>>> o + 0 == o && 0 + o == o && o + 1 /= o
True
>>> o - 0 == o && 0 - o /= o
True
>>> o / 1 == o && 1 / o /= o
True
>>> o ** 1 == o && o ** 2 /= o
True
-}
mkBinaryOperator_optimise_constants ::
Sc3_Binary_Op ->
(Sample -> Sample -> Sample) ->
(Either Sample Sample -> Bool) ->
Ugen ->
Ugen ->
Ugen
mkBinaryOperator_optimise_constants i f o a b =
let g [x, y] = f x y
g _ = error "mkBinaryOperator: non binary input"
r = case (a, b) of
(Constant_U (Constant a' ([], [])), _) ->
if o (Left a') then Just b else Nothing
(_, Constant_U (Constant b' ([], []))) ->
if o (Right b') then Just a else Nothing
_ -> Nothing
in fromMaybe (mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)) r
-- | Plain (non-optimised) binary math constructor.
mkBinaryOperator :: Sc3_Binary_Op -> (Sample -> Sample -> Sample) -> Ugen -> Ugen -> Ugen
mkBinaryOperator i f a b =
let g [x, y] = f x y
g _ = error "mkBinaryOperator: non binary input"
in mkOperator g "BinaryOpUGen" [a, b] (fromEnum i)
-- * Numeric instances
-- | Is /u/ the primitive for the named Ugen.
is_primitive_for :: String -> Ugen -> Bool
is_primitive_for k u =
case u of
Primitive_U (Primitive _ nm [_, _] [_] _ _ _) -> nm == k
_ -> False
-- | Is /u/ the primitive for the named Ugen.
is_constant_of :: Sample -> Ugen -> Bool
is_constant_of k u =
case u of
Constant_U c -> constantValue c == k
_ -> False
-- | Is /u/ a binary math operator with SPECIAL of /k/.
is_math_binop :: Int -> Ugen -> Bool
is_math_binop k u =
case u of
Primitive_U (Primitive _ "BinaryOpUGen" [_, _] [_] (Special s) NoId _) -> s == k
_ -> False
-- | Is /u/ an ADD operator?
is_add_operator :: Ugen -> Bool
is_add_operator = is_math_binop 0
assert_is_add_operator :: String -> Ugen -> Ugen
assert_is_add_operator msg u = if is_add_operator u then u else error ("assert_is_add_operator: " ++ msg)
-- | Is /u/ an MUL operator?
is_mul_operator :: Ugen -> Bool
is_mul_operator = is_math_binop 2
{- | MulAdd re-writer, applicable only directly at add operator Ugen.
The MulAdd Ugen is very sensitive to input rates.
Add=AudioRate with In|Mul=InitialisationRate|Const will crash scsynth.
This only considers primitives that do not have bracketing messages.
-}
mul_add_optimise_direct :: Ugen -> Ugen
mul_add_optimise_direct u =
let reorder (i, j, k) =
let (ri, rj, rk) = (rateOf i, rateOf j, rateOf k)
in if rk > max ri rj
then Nothing
else Just (max (max ri rj) rk, if rj > ri then (j, i, k) else (i, j, k))
in case assert_is_add_operator "MUL-ADD" u of
Primitive_U
(Primitive _ _ [Primitive_U (Primitive _ "BinaryOpUGen" [i, j] [_] (Special 2) NoId ([], [])), k] [_] _ NoId ([], [])) ->
case reorder (i, j, k) of
Just (rt, (p, q, r)) -> Primitive_U (Primitive rt "MulAdd" [p, q, r] [rt] (Special 0) NoId ([], []))
Nothing -> u
Primitive_U
(Primitive _ _ [k, Primitive_U (Primitive _ "BinaryOpUGen" [i, j] [_] (Special 2) NoId ([], []))] [_] _ NoId ([], [])) ->
case reorder (i, j, k) of
Just (rt, (p, q, r)) -> Primitive_U (Primitive rt "MulAdd" [p, q, r] [rt] (Special 0) NoId ([], []))
Nothing -> u
_ -> u
{- | MulAdd optimiser, applicable at any Ugen (ie. checks /u/ is an ADD ugen)
> import Sound.Sc3
> g1 = sinOsc ar 440 0 * 0.1 + control ir "x" 0.05
> g2 = sinOsc ar 440 0 * control ir "x" 0.1 + 0.05
> g3 = control ir "x" 0.1 * sinOsc ar 440 0 + 0.05
> g4 = 0.05 + sinOsc ar 440 0 * 0.1
-}
mul_add_optimise :: Ugen -> Ugen
mul_add_optimise u = if is_add_operator u then mul_add_optimise_direct u else u
{- | Sum3 re-writer, applicable only directly at add operator Ugen.
This only considers nodes that have no bracketing messages.
-}
sum3_optimise_direct :: Ugen -> Ugen
sum3_optimise_direct u =
case assert_is_add_operator "SUM3" u of
Primitive_U (Primitive r _ [Primitive_U (Primitive _ "BinaryOpUGen" [i, j] [_] (Special 0) NoId ([], [])), k] [_] _ NoId ([], [])) ->
Primitive_U (Primitive r "Sum3" [i, j, k] [r] (Special 0) NoId ([], []))
Primitive_U (Primitive r _ [k, Primitive_U (Primitive _ "BinaryOpUGen" [i, j] [_] (Special 0) NoId ([], []))] [_] _ NoId ([], [])) ->
Primitive_U (Primitive r "Sum3" [i, j, k] [r] (Special 0) NoId ([], []))
_ -> u
-- | /Sum3/ optimiser, applicable at any /u/ (ie. checks if /u/ is an ADD operator).
sum3_optimise :: Ugen -> Ugen
sum3_optimise u = if is_add_operator u then sum3_optimise_direct u else u
-- | 'sum3_optimise' of 'mul_add_optimise'.
add_optimise :: Ugen -> Ugen
add_optimise = sum3_optimise . mul_add_optimise
-- | Unit generators are numbers.
instance Num Ugen where
negate = mkUnaryOperator OpNeg negate
(+) =
fmap add_optimise
. mkBinaryOperator_optimise_constants OpAdd (+) (`elem` [Left 0, Right 0])
(-) = mkBinaryOperator_optimise_constants OpSub (-) (Right 0 ==)
(*) = mkBinaryOperator_optimise_constants OpMul (*) (`elem` [Left 1, Right 1])
abs = mkUnaryOperator OpAbs abs
signum = mkUnaryOperator OpSign signum
fromInteger = Constant_U . flip Constant ([], []) . fromInteger
-- | Unit generators are fractional.
instance Fractional Ugen where
recip = mkUnaryOperator OpRecip recip
(/) = mkBinaryOperator_optimise_constants OpFdiv (/) (Right 1 ==)
fromRational = Constant_U . flip Constant ([], []) . fromRational
-- | Unit generators are floating point.
instance Floating Ugen where
pi = Constant_U (Constant pi ([], []))
exp = mkUnaryOperator OpExp exp
log = mkUnaryOperator OpLog log
sqrt = mkUnaryOperator OpSqrt sqrt
(**) = mkBinaryOperator_optimise_constants OpPow (**) (Right 1 ==)
logBase a b = log b / log a
sin = mkUnaryOperator OpSin sin
cos = mkUnaryOperator OpCos cos
tan = mkUnaryOperator OpTan tan
asin = mkUnaryOperator OpArcSin asin
acos = mkUnaryOperator OpArcCos acos
atan = mkUnaryOperator OpArcTan atan
sinh = mkUnaryOperator OpSinh sinh
cosh = mkUnaryOperator OpCosh cosh
tanh = mkUnaryOperator OpTanh tanh
asinh x = log (sqrt (x * x + 1) + x)
acosh x = log (sqrt (x * x - 1) + x)
atanh x = (log (1 + x) - log (1 - x)) / 2
-- | Unit generators are real.
instance Real Ugen where
toRational (Constant_U (Constant n ([], []))) = toRational n
toRational _ = error "Ugen.toRational: only un-bracketed constants considered"
-- | Unit generators are integral.
instance Integral Ugen where
quot = mkBinaryOperator OpIdiv (error "Ugen.quot")
rem = mkBinaryOperator OpMod (error "Ugen.rem")
quotRem a b = (quot a b, rem a b)
div = mkBinaryOperator OpIdiv (error "Ugen.div")
mod = mkBinaryOperator OpMod (error "Ugen.mod")
toInteger (Constant_U (Constant n ([], []))) = floor n
toInteger _ = error "Ugen.toInteger: only un-bracketed constants considered"
instance RealFrac Ugen where
properFraction = error "Ugen.properFraction, see properFractionE"
round = error "Ugen.round, see roundE"
ceiling = error "Ugen.ceiling, see ceilingE"
floor = error "Ugen.floor, see floorE"
{- | Unit generators are orderable (when 'Constants').
>>> constant 2 > constant 1
True
-}
instance Ord Ugen where
(Constant_U a) < (Constant_U b) = a < b
_ < _ = error "Ugen.<, see <*"
(Constant_U a) <= (Constant_U b) = a <= b
_ <= _ = error "Ugen.<= at, see <=*"
(Constant_U a) > (Constant_U b) = a > b
_ > _ = error "Ugen.>, see >*"
(Constant_U a) >= (Constant_U b) = a >= b
_ >= _ = error "Ugen.>=, see >=*"
min = mkBinaryOperator OpMin min
max = mkBinaryOperator OpMax max
-- | Unit generators are enumerable.
instance Enum Ugen where
succ u = u + 1
pred u = u - 1
toEnum n = Constant_U (Constant (fromIntegral n) ([], []))
fromEnum (Constant_U (Constant n ([], []))) = truncate n
fromEnum _ = error "Ugen.fromEnum: non-constant"
enumFrom = iterate (+ 1)
enumFromThen n m = iterate (+ (m - n)) n
enumFromTo n m = takeWhile (<= m + 1 / 2) (enumFrom n)
enumFromThenTo n n' m =
let p = if n' >= n then (>=) else (<=)
in takeWhile (p (m + (n' - n) / 2)) (enumFromThen n n')
{- | Unit generators are stochastic.
Only un-bracketed constant values are considered.
-}
instance Random.Random Ugen where
randomR (Constant_U (Constant l ([], [])), Constant_U (Constant r ([], []))) g =
let (n, g') = Random.randomR (l, r) g
in (Constant_U (Constant n ([], [])), g')
randomR _ _ = error "Ugen.randomR: non constant (l,r)"
random = Random.randomR (-1.0, 1.0)
-- * Bitwise
-- | 'Operator.OpBitAnd'
bitAnd :: Ugen -> Ugen -> Ugen
bitAnd = mkBinaryOperator OpBitAnd undefined
-- | 'Operator.OpBitOr'
bitOr :: Ugen -> Ugen -> Ugen
bitOr = mkBinaryOperator OpBitOr undefined
-- | 'OpBitXor'
bitXOr :: Ugen -> Ugen -> Ugen
bitXOr = mkBinaryOperator OpBitXor undefined
-- | 'OpBitNot'
bitNot :: Ugen -> Ugen
bitNot = mkUnaryOperator OpBitNot undefined
-- | 'OpShiftLeft'
shiftLeft :: Ugen -> Ugen -> Ugen
shiftLeft = mkBinaryOperator OpShiftLeft undefined
-- | 'OpShiftRight'
shiftRight :: Ugen -> Ugen -> Ugen
shiftRight = mkBinaryOperator OpShiftRight undefined
-- | 'OpUnsignedShift'
unsignedShift :: Ugen -> Ugen -> Ugen
unsignedShift = mkBinaryOperator OpUnsignedShift undefined
-- | Ugens are bit patterns.
instance Bits Ugen where
(.&.) = bitAnd
(.|.) = bitOr
xor = bitXOr
complement = bitNot
shiftL p q = shiftLeft p (constant q)
shiftR p q = shiftRight p (constant q)
rotate = error "Ugen.rotate"
bitSize = error "Ugen.bitSize"
bit = error "Ugen.bit"
testBit = error "Ugen.testBit"
popCount = error "Ugen.popCount" -- hugs...
bitSizeMaybe = error "Ugen.bitSizeMaybe" -- hugs...
isSigned _ = True
{-
import qualified GHC.Exts as Exts {- base -}
instance Exts.IsList Ugen where
type Item Ugen = Ugen
fromList = mce
toList = mceChannels
-}