hsc3-0.19: Sound/SC3/UGen/Type.hs
-- | Unit Generator ('UGen') and associated types and instances.
module Sound.SC3.UGen.Type where
import Data.Bits {- base -}
import Data.Either {- base -}
import qualified Data.Fixed as F {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}
import Text.Printf {- base -}
import qualified Safe {- safe -}
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.Rate
import Sound.SC3.UGen.MCE
-- * Basic types
-- | Type of unique identifier.
type UID_t = Int
-- | Data type for the identifier at a 'Primitive' 'UGen'.
data UGenId = NoId | UId UID_t
deriving (Eq,Read,Show)
-- | Alias of 'NoId', the 'UGenId' used for deterministic UGens.
no_id :: UGenId
no_id = NoId
-- | SC3 samples are 32-bit 'Float'. hsc3 represents data as 64-bit
-- 'Double'. If 'UGen' values are used more generally (ie. see
-- hsc3-forth) 'Float' may be too imprecise, ie. for representing time
-- stamps.
type Sample = Double
-- | Constants.
--
-- > Constant 3 == Constant 3
-- > (Constant 3 > Constant 1) == True
newtype Constant = Constant {constantValue :: Sample} deriving (Eq,Ord,Read,Show)
-- | Control meta-data.
data Control_Meta n =
Control_Meta {ctl_min :: n -- ^ Minimum
,ctl_max :: n -- ^ Maximum
,ctl_warp :: String -- ^ @(0,1)@ @(min,max)@ transfer function.
,ctl_step :: n -- ^ The step to increment & decrement by.
,ctl_units :: String -- ^ Unit of measure (ie hz, ms etc.).
,controlGroup :: Maybe Control_Group -- ^ Control group.
}
deriving (Eq,Read,Show)
-- | 3-tuple form of 'Control_Meta' data.
type Control_Meta_T3 n = (n,n,String)
-- | Lift 'Control_Meta_T3' to 'Control_Meta' allowing type coercion.
control_meta_t3 :: Num m => (n -> m) -> Control_Meta_T3 n -> Control_Meta m
control_meta_t3 f (l,r,w) = Control_Meta (f l) (f r) w 0 "" Nothing
-- | 5-tuple form of 'Control_Meta' data.
type Control_Meta_T5 n = (n,n,String,n,String)
-- | Lift 'Control_Meta_T5' to 'Control_Meta' allowing type coercion.
control_meta_t5 :: (n -> m) -> Control_Meta_T5 n -> Control_Meta m
control_meta_t5 f (l,r,w,stp,u) = Control_Meta (f l) (f r) w (f stp) u Nothing
{- | Controls may form part of a control group. -}
data Control_Group
= Control_Range
| Control_Array Int
| Control_XY
deriving (Eq,Read,Show)
-- | The number of elements in a control group.
control_group_degree :: Control_Group -> Int
control_group_degree grp =
case grp of
Control_Range -> 2
Control_Array n -> n
Control_XY -> 2
{- | Grouped controls have names that have equal prefixes and identifying suffixes.
Range controls have two elements, minima and maxima, suffixes are [ and ].
Array controls have N elements and have IX suffixes.
XY controls have two elements, X and Y coordinates, suffixes are X and Y.
-}
control_group_suffixes :: Control_Group -> [String]
control_group_suffixes grp =
case grp of
Control_Range -> ["[","]"]
Control_Array n -> map (printf "%02d") [0 .. n - 1]
Control_XY -> ["X","Y"]
-- | Control inputs. It is an invariant that controls with equal
-- names within a UGen graph must be equal in all other respects.
data Control = Control {controlOperatingRate :: Rate
,controlIndex :: Maybe Int
,controlName :: String
,controlDefault :: Sample
,controlTriggered :: Bool
,controlMeta :: Maybe (Control_Meta Sample)}
deriving (Eq,Read,Show)
-- | Labels.
newtype Label = Label {ugenLabel :: String} deriving (Eq,Read,Show)
-- | Unit generator output descriptor.
type Output = Rate
-- | Operating mode of unary and binary operators.
newtype Special = Special Int
deriving (Eq,Read,Show)
-- | UGen primitives.
data Primitive = Primitive {ugenRate :: Rate
,ugenName :: String
,ugenInputs :: [UGen]
,ugenOutputs :: [Output]
,ugenSpecial :: Special
,ugenId :: UGenId}
deriving (Eq,Read,Show)
-- | Proxy indicating an output port at a multi-channel primitive.
data Proxy = Proxy {proxySource :: Primitive
,proxyIndex :: Int}
deriving (Eq,Read,Show)
-- | Multiple root graph.
data MRG = MRG {mrgLeft :: UGen
,mrgRight :: UGen}
deriving (Eq,Read,Show)
-- | Union type of Unit Generator forms.
data UGen = Constant_U Constant
| Control_U Control
| Label_U Label
| Primitive_U Primitive
| Proxy_U Proxy
| MCE_U (MCE UGen)
| MRG_U MRG
deriving (Eq,Read,Show)
instance EqE UGen where
equal_to = mkBinaryOperator EQ_ Math.sc3_eq
not_equal_to = mkBinaryOperator NE Math.sc3_neq
instance OrdE UGen where
less_than = mkBinaryOperator LT_ Math.sc3_lt
less_than_or_equal_to = mkBinaryOperator LE Math.sc3_lte
greater_than = mkBinaryOperator GT_ Math.sc3_gt
greater_than_or_equal_to = mkBinaryOperator GE Math.sc3_gte
-- | 'UGen' form or 'Math.sc3_round_to'.
roundTo :: UGen -> UGen -> UGen
roundTo = mkBinaryOperator Round Math.sc3_round_to
instance RealFracE UGen where
properFractionE = error "UGen.properFractionE"
truncateE = error "UGen.truncateE"
roundE i = roundTo i 1
ceilingE = mkUnaryOperator Ceil ceilingE
floorE = mkUnaryOperator Floor floorE
instance UnaryOp UGen where
ampDb = mkUnaryOperator AmpDb ampDb
asFloat = mkUnaryOperator AsFloat asFloat
asInt = mkUnaryOperator AsInt asInt
cpsMIDI = mkUnaryOperator CPSMIDI cpsMIDI
cpsOct = mkUnaryOperator CPSOct cpsOct
cubed = mkUnaryOperator Cubed cubed
dbAmp = mkUnaryOperator DbAmp dbAmp
distort = mkUnaryOperator Distort distort
frac = mkUnaryOperator Frac frac
isNil = mkUnaryOperator IsNil isNil
log10 = mkUnaryOperator Log10 log10
log2 = mkUnaryOperator Log2 log2
midiCPS = mkUnaryOperator MIDICPS midiCPS
midiRatio = mkUnaryOperator MIDIRatio midiRatio
notE = mkUnaryOperator Not notE
notNil = mkUnaryOperator NotNil notNil
octCPS = mkUnaryOperator OctCPS octCPS
ramp_ = mkUnaryOperator Ramp_ ramp_
ratioMIDI = mkUnaryOperator RatioMIDI ratioMIDI
softClip = mkUnaryOperator SoftClip softClip
squared = mkUnaryOperator Squared squared
instance BinaryOp UGen where
iDiv = mkBinaryOperator IDiv iDiv
modE = mkBinaryOperator Mod F.mod'
lcmE = mkBinaryOperator LCM lcmE
gcdE = mkBinaryOperator GCD gcdE
roundUp = mkBinaryOperator RoundUp roundUp
trunc = mkBinaryOperator Trunc trunc
atan2E = mkBinaryOperator Atan2 atan2E
hypot = mkBinaryOperator Hypot hypot
hypotx = mkBinaryOperator Hypotx hypotx
fill = mkBinaryOperator Fill fill
ring1 = mkBinaryOperator Ring1 ring1
ring2 = mkBinaryOperator Ring2 ring2
ring3 = mkBinaryOperator Ring3 ring3
ring4 = mkBinaryOperator Ring4 ring4
difSqr = mkBinaryOperator DifSqr difSqr
sumSqr = mkBinaryOperator SumSqr sumSqr
sqrSum = mkBinaryOperator SqrSum sqrSum
sqrDif = mkBinaryOperator SqrDif sqrDif
absDif = mkBinaryOperator AbsDif absDif
thresh = mkBinaryOperator Thresh thresh
amClip = mkBinaryOperator AMClip amClip
scaleNeg = mkBinaryOperator ScaleNeg scaleNeg
clip2 = mkBinaryOperator Clip2 clip2
excess = mkBinaryOperator Excess excess
fold2 = mkBinaryOperator Fold2 fold2
wrap2 = mkBinaryOperator Wrap2 wrap2
firstArg = mkBinaryOperator FirstArg firstArg
randRange = mkBinaryOperator RandRange randRange
exprandRange = mkBinaryOperator ExpRandRange 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_elem m)
_ -> False
-- | See into 'Proxy_U'.
un_proxy :: UGen -> Maybe Proxy
un_proxy u =
case u of
Proxy_U p -> Just p
_ -> Nothing
-- | Is 'UGen' a 'Proxy'?
isProxy :: UGen -> Bool
isProxy = isJust . un_proxy
-- * MCE
-- | Multiple channel expansion node constructor.
mce :: [UGen] -> UGen
mce xs =
case xs of
[] -> error "mce: []"
[x] -> x
_ -> MCE_U (MCE_Vector xs)
-- | Type specified 'mce_elem'.
mceProxies :: MCE UGen -> [UGen]
mceProxies = mce_elem
-- | 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 and MRG node in channel 0.
mceChannels :: UGen -> [UGen]
mceChannels u =
case u of
MCE_U m -> mce_elem m
MRG_U (MRG x y) -> let r:rs = mceChannels x in MRG_U (MRG r y) : rs
_ -> [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) -> let (r:rs) = mceExtend n x
in MRG_U (MRG r y) : rs
_ -> 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 [mce2 1 2,mce2 3 4] == Just [[1,3],[2,4]]
> mceInputTransform [mce2 1 2,mce2 3 4,mce3 5 6 7] == Just [[1,3,5],[2,4,6],[1,3,7]]
-}
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' -> MCE_U (MCE_Vector (map (mceBuild f) i'))
-- | 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.
mce_is_direct_proxy :: MCE UGen -> Bool
mce_is_direct_proxy m =
case m of
MCE_Unit _ -> False
MCE_Vector v ->
let p = map un_proxy v
p' = catMaybes p
in all isJust p &&
length (nub (map proxySource p')) == 1 &&
map proxyIndex p' `isPrefixOf` [0..]
-- * 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 . Constant . 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 _ -> IR
Control_U c -> controlOperatingRate c
Label_U _ -> IR
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_elem 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 DR inputs run at KR. 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 r' = either id (maximum . map (rateOf . Safe.atNote ("mkUGen: " ++ nm) h)) r
in if isRight r && r' == DR && DR `notElem` rs
then if KR `elem` rs then KR else error "mkUGen: DR input to non-KR filter"
else if r' `elem` rs || r' == DR
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)
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
--
-- > let o = sinOsc AR 440 0
--
-- > o * 1 == o && 1 * o == o && o * 2 /= o
-- > o + 0 == o && 0 + o == o && o + 1 /= o
-- > o - 0 == o && 0 - o /= o
-- > o / 1 == o && 1 / o /= o
-- > o ** 1 == o && o ** 2 /= o
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/ 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=AR with IN|MUL=IR|CONST will CRASH scsynth.
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.
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 Neg negate
(+) = fmap add_optimise .
mkBinaryOperator_optimise_constants Add (+) (`elem` [Left 0,Right 0])
(-) = mkBinaryOperator_optimise_constants Sub (-) (Right 0 ==)
(*) = mkBinaryOperator_optimise_constants Mul (*) (`elem` [Left 1,Right 1])
abs = mkUnaryOperator Abs abs
signum = mkUnaryOperator Sign signum
fromInteger = Constant_U . Constant . fromInteger
-- | Unit generators are fractional.
instance Fractional UGen where
recip = mkUnaryOperator Recip recip
(/) = mkBinaryOperator_optimise_constants FDiv (/) (Right 1 ==)
fromRational = Constant_U . Constant . fromRational
-- | Unit generators are floating point.
instance Floating UGen where
pi = Constant_U (Constant pi)
exp = mkUnaryOperator Exp exp
log = mkUnaryOperator Log log
sqrt = mkUnaryOperator Sqrt sqrt
(**) = mkBinaryOperator_optimise_constants Pow (**) (Right 1 ==)
logBase a b = log b / log a
sin = mkUnaryOperator Sin sin
cos = mkUnaryOperator Cos cos
tan = mkUnaryOperator Tan tan
asin = mkUnaryOperator ArcSin asin
acos = mkUnaryOperator ArcCos acos
atan = mkUnaryOperator ArcTan atan
sinh = mkUnaryOperator SinH sinh
cosh = mkUnaryOperator CosH cosh
tanh = mkUnaryOperator TanH 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: non-constant"
-- | Unit generators are integral.
instance Integral UGen where
quot = mkBinaryOperator IDiv (error "UGen.quot")
rem = mkBinaryOperator Mod (error "UGen.rem")
quotRem a b = (quot a b, rem a b)
div = mkBinaryOperator IDiv (error "UGen.div")
mod = mkBinaryOperator Mod (error "UGen.mod")
toInteger (Constant_U (Constant n)) = floor n
toInteger _ = error "UGen.toInteger: non-constant"
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 Min min
max = mkBinaryOperator Max 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.
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)
-- | UGens are bit patterns.
instance Bits UGen where
(.&.) = mkBinaryOperator BitAnd undefined
(.|.) = mkBinaryOperator BitOr undefined
xor = mkBinaryOperator BitXor undefined
complement = mkUnaryOperator BitNot undefined
shift = error "UGen.shift"
rotate = error "UGen.rotate"
bitSize = error "UGen.bitSize"
bit = error "UGen.bit"
testBit = error "UGen.testBit"
popCount = error "UGen.popCount"
bitSizeMaybe = error "UGen.bitSizeMaybe"
isSigned _ = True