-- | Non-standard mathematical classes and class instances. module Sound.SC3.UGen.Math where import qualified Data.Fixed as F {- base -} import Data.Int import Sound.SC3.UGen.Operator import Sound.SC3.UGen.Type -- The Eq and Ord classes in the Prelude require Bool, hence the name -- mangling. True is 1.0, False is 0.0 -- | Variant on Eq class, result is of the same type as the values compared. class (Eq a,Num a) => EqE a where (==*) :: a -> a -> a a ==* b = if a == b then 1 else 0 (/=*) :: a -> a -> a a /=* b = if a /= b then 1 else 0 instance EqE Int where instance EqE Integer where instance EqE Int32 where instance EqE Int64 where instance EqE Float where instance EqE Double where instance EqE UGen where (==*) = mkBinaryOperator EQ_ (==*) (/=*) = mkBinaryOperator NE (/=*) -- | Variant on Ord class, result is of the same type as the values compared. class (Ord a,Num a) => OrdE a where (<*) :: a -> a -> a a <* b = if a < b then 1 else 0 (<=*) :: a -> a -> a a <=* b = if a <= b then 1 else 0 (>*) :: a -> a -> a a >* b = if a > b then 1 else 0 (>=*) :: a -> a -> a a >=* b = if a >= b then 1 else 0 instance OrdE Int instance OrdE Integer instance OrdE Int32 where instance OrdE Int64 where instance OrdE Float instance OrdE Double instance OrdE UGen where (<*) = mkBinaryOperator LT_ (<*) (<=*) = mkBinaryOperator LE (<=*) (>*) = mkBinaryOperator GT_ (>*) (>=*) = mkBinaryOperator GE (>=*) -- | Variant of 'RealFrac' with non 'Integral' results. class RealFrac a => RealFracE a where properFractionE :: a -> (a,a) properFractionE a = let (p,q) = properFraction a in (fromInteger p,q) truncateE :: a -> a truncateE a = fromInteger (truncate a) roundE :: a -> a roundE a = fromInteger (round a) ceilingE :: a -> a ceilingE a = fromInteger (ceiling a) floorE :: a -> a floorE a = fromInteger (floor a) instance RealFracE Float instance RealFracE Double -- | Variant of @SC3@ @roundTo@ function. roundTo_ :: RealFracE a => a -> a -> a roundTo_ a b = if b == 0 then a else floorE (a/b + 0.5) * b -- | 'UGen' form or 'roundTo_'. roundTo :: UGen -> UGen -> UGen roundTo = mkBinaryOperator Round roundTo_ 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 -- | 'UGen' form of 'ceilingE'. ceil :: UGen -> UGen ceil = ceilingE -- | 'Floating' form of 'midiCPS'. midiCPS' :: Floating a => a -> a midiCPS' i = 440.0 * (2.0 ** ((i - 69.0) * (1.0 / 12.0))) -- | Unary operator class. -- -- > map (floor . (* 1e4) . dbAmp) [-90,-60,-30,0] == [0,10,316,10000] class (Floating a, Ord a) => UnaryOp a where ampDb :: a -> a ampDb a = log10 a * 20 asFloat :: a -> a asFloat = error "asFloat" asInt :: a -> a asInt = error "asInt" cpsMIDI :: a -> a cpsMIDI a = (log2 (a * (1.0 / 440.0)) * 12.0) + 69.0 cpsOct :: a -> a cpsOct a = log2 (a * (1.0 / 440.0)) + 4.75 cubed :: a -> a cubed a = a * a * a dbAmp :: a -> a dbAmp a = 10 ** (a * 0.05) distort :: a -> a distort = error "distort" frac :: a -> a frac = error "frac" isNil :: a -> a isNil a = if a == 0.0 then 0.0 else 1.0 log10 :: a -> a log10 = logBase 10 log2 :: a -> a log2 = logBase 2 midiCPS :: a -> a midiCPS = midiCPS' midiRatio :: a -> a midiRatio a = 2.0 ** (a * (1.0 / 12.0)) notE :: a -> a notE a = if a > 0.0 then 0.0 else 1.0 notNil :: a -> a notNil a = if a /= 0.0 then 0.0 else 1.0 octCPS :: a -> a octCPS a = 440.0 * (2.0 ** (a - 4.75)) ramp_ :: a -> a ramp_ _ = error "ramp_" ratioMIDI :: a -> a ratioMIDI a = 12.0 * log2 a softClip :: a -> a softClip = error "softClip" squared :: a -> a squared a = a * a instance UnaryOp Float where instance UnaryOp Double where 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 -- | Binary operator class. class (Floating a, Ord a) => BinaryOp a where absDif :: a -> a -> a absDif a b = abs (a - b) amClip :: a -> a -> a amClip a b = if b <= 0 then 0 else a * b atan2E :: a -> a -> a atan2E a b = atan (b/a) clip2 :: a -> a -> a clip2 a b = clip_ a (-b) b difSqr :: a -> a -> a difSqr a b = (a*a) - (b*b) excess :: a -> a -> a excess a b = a - clip_ a (-b) b exprandRange :: a -> a -> a exprandRange = error "exprandRange" fill :: a -> a -> a fill = error "fill" firstArg :: a -> a -> a firstArg a _ = a fold2 :: a -> a -> a fold2 a b = fold_ a (-b) b gcdE :: a -> a -> a gcdE = error "gcdE" hypot :: a -> a -> a hypot = error "hypot" hypotx :: a -> a -> a hypotx = error "hypotx" iDiv :: a -> a -> a iDiv = error "iDiv" lcmE :: a -> a -> a lcmE = error "lcmE" modE :: a -> a -> a modE = error "modE" randRange :: a -> a -> a randRange = error "randRange" ring1 :: a -> a -> a ring1 a b = a * b + a ring2 :: a -> a -> a ring2 a b = a * b + a + b ring3 :: a -> a -> a ring3 a b = a * a * b ring4 :: a -> a -> a ring4 a b = a * a * b - a * b * b roundUp :: a -> a -> a roundUp = error "roundUp" scaleNeg :: a -> a -> a scaleNeg a b = (abs a - a) * b' + a where b' = 0.5 * b + 0.5 sqrDif :: a -> a -> a sqrDif a b = (a-b) * (a-b) sqrSum :: a -> a -> a sqrSum a b = (a+b) * (a+b) sumSqr :: a -> a -> a sumSqr a b = (a*a) + (b*b) thresh :: a -> a -> a thresh a b = if a < b then 0 else a trunc :: a -> a -> a trunc = error "trunc" wrap2 :: a -> a -> a wrap2 = error "wrap2" -- | The SC3 @%@ operator is libc fmod function. -- -- > 1.5 % 1.2 // ~= 0.3 -- > -1.5 % 1.2 // ~= 0.9 -- > 1.5 % -1.2 // ~= -0.9 -- > -1.5 % -1.2 // ~= -0.3 -- -- > 1.5 `fmod` 1.2 -- ~= 0.3 -- > (-1.5) `fmod` 1.2 -- ~= 0.9 -- > 1.5 `fmod` (-1.2) -- ~= -0.9 -- > (-1.5) `fmod` (-1.2) -- ~= -0.3 -- -- 1.2 % 1.5 // ~= 1.2 -- -1.2 % 1.5 // ~= 0.3 -- 1.2 % -1.5 // ~= -0.3 -- -1.2 % -1.5 // ~= -1.2 -- -- > 1.2 `fmod` 1.5 -- ~= 1.2 -- > (-1.2) `fmod` 1.5 -- ~= 0.3 -- > 1.2 `fmod` (-1.5) -- ~= -0.3 -- > (-1.2) `fmod` (-1.5) -- ~= -1.2 fmod_f32 :: Float -> Float -> Float fmod_f32 = F.mod' instance BinaryOp Float where fold2 a b = fold_ a (-b) b modE = F.mod' roundUp a b = if b == 0 then a else ceilingE (a/b + 0.5) * b wrap2 a b = wrap_ a (-b) b instance BinaryOp Double where fold2 a b = fold_ a (-b) b modE = F.mod' roundUp a b = if b == 0 then a else ceilingE (a/b + 0.5) * b wrap2 a b = wrap_ a (-b) b 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 -- | Wrap /k/ to within range /(i,j)/, ie. @AbstractFunction.wrap@. -- -- > > [5,6].wrap(0,5) == [5,0] -- > map (wrap' 0 5) [5,6] == [5,0] -- -- > > [9,10,5,6,7,8,9,10,5,6].wrap(5,10) == [9,10,5,6,7,8,9,10,5,6] -- > map (wrap' 5 10) [3..12] == [9,10,5,6,7,8,9,10,5,6] wrap' :: RealFracE n => n -> n -> n -> n wrap' i j k = let r = j - i + 1 in if k >= i && k <= j then k else k - r * floorE ((k-i) / r) -- | Generic variant of 'wrap''. -- -- > > [5,6].wrap(0,5) == [5,0] -- > map (genericWrap 0 5) [5,6] == [5,0] -- -- > > [9,10,5,6,7,8,9,10,5,6].wrap(5,10) == [9,10,5,6,7,8,9,10,5,6] -- > map (genericWrap (5::Integer) 10) [3..12] == [9,10,5,6,7,8,9,10,5,6] genericWrap :: (Ord a, Num a) => a -> a -> a -> a genericWrap l r n = let d = r - l + 1 f = genericWrap l r in if n < l then f (n + d) else if n > r then f (n - d) else n -- | Variant of 'wrap'' with @SC3@ argument ordering. -- -- > map (\n -> wrap_ n 5 10) [3..12] == map (wrap' 5 10) [3..12] wrap_ :: RealFracE n => n -> n -> n -> n wrap_ a b c = wrap' b c a -- | Fold /k/ to within range /(i,j)/, ie. @AbstractFunction.fold@ -- -- > map (foldToRange 5 10) [3..12] == [7,6,5,6,7,8,9,10,9,8] foldToRange :: (Ord a,Num a) => a -> a -> a -> a foldToRange i j = let f n = if n > j then f (j - (n - j)) else if n < i then f (i - (n - i)) else n in f -- | Variant of 'foldToRange' with @SC3@ argument ordering. fold_ :: (Ord a,Num a) => a -> a -> a -> a fold_ n i j = foldToRange i j n -- | Clip /k/ to within range /(i,j)/, -- -- > map (clip' 5 10) [3..12] == [5,5,5,6,7,8,9,10,10,10] clip' :: (Ord a) => a -> a -> a -> a clip' i j n = if n < i then i else if n > j then j else n -- | Variant of 'clip'' with @SC3@ argument ordering. clip_ :: (Ord a) => a -> a -> a -> a clip_ n i j = clip' i j n