-- | 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 -}