packages feed

zwirn-0.2.2.0: src/zwirn-core/Zwirn/Core/Lib/Random.hs

module Zwirn.Core.Lib.Random where

{-
    Random.hs - simple random signals and related functions
    Copyright (C) 2025, Martin Gius

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this library.  If not, see <http://www.gnu.org/licenses/>.
-}

import Control.Monad (join)
import qualified Numeric.Noise as Noise
import System.Random
import Zwirn.Core.Core
import Zwirn.Core.Lib.Core
import Zwirn.Core.Lib.Modulate
import Zwirn.Core.Time (Time)
import Zwirn.Core.Types

precision :: Time
precision = 0.005

randR :: (Random a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a
randR l r = zwirn q
  where
    q t = unzwirn (fmap fst $ liftA2 randomR zipp $ pure $ mkStdGen $ floor (t / precision)) t
      where
        zipp = liftA2 (,) l r

rand :: (Random a, Applicative k) => ZwirnT k st i a
rand = zwirn $ \t st -> pure (Value (fst $ random (mkStdGen $ floor (t / precision))) t [], st)

noise :: (Applicative k) => ZwirnT k st i Double
noise = rand

irand :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int
irand = randR (pure 0)

brandBy :: (Applicative k) => ZwirnT k st i Double -> ZwirnT k st i Bool
brandBy prob = liftA2 (>) prob rand

sometimesBy :: (MultiMonad k) => ZwirnT k st i Double -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
sometimesBy prob f x = innerJoin $ fmap cho (brandBy prob)
  where
    cho True = apply f x
    cho False = x

sometimes :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
sometimes = sometimesBy (pure 0.5)

often :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
often = sometimesBy (pure 0.75)

rarely :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
rarely = sometimesBy (pure 0.25)

almostNever :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
almostNever = sometimesBy (pure 0.1)

almostAlways :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
almostAlways = sometimesBy (pure 0.9)

-- TODO: FIX
degradeBy :: (MultiMonad k, HasSilence k) => ZwirnT k st i Double -> ZwirnT k st i a -> ZwirnT k st i a
degradeBy prob = sometimesBy prob (pure $ const silence)

degrade :: (MultiMonad k, HasSilence k) => ZwirnT k st i a -> ZwirnT k st i a
degrade = sometimes (pure $ const silence)

-- these versions take the cycle number as seed

randR' :: (Random a, Applicative k) => ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a
randR' r l = zwirn q
  where
    q t = unzwirn (fmap fst $ liftA2 randomR zipp $ pure $ mkStdGen $ floor t) t
      where
        zipp = liftA2 (,) l r

rand' :: (Random a, Applicative k) => ZwirnT k st i a
rand' = zwirn $ \t st -> pure (Value (fst $ random (mkStdGen $ floor t)) t [], st)

irand' :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Int
irand' = randR' (pure 0)

brandBy' :: (Applicative k) => ZwirnT k st i Double -> ZwirnT k st i Bool
brandBy' prob = liftA2 (>) prob rand'

somecyclesBy :: (MultiMonad k) => ZwirnT k st i Double -> ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
somecyclesBy prob f x = innerJoin $ fmap cho (brandBy' prob)
  where
    cho True = apply f x
    cho False = x

somecycles :: (MultiMonad k) => ZwirnT k st i (ZwirnT k st i a -> ZwirnT k st i a) -> ZwirnT k st i a -> ZwirnT k st i a
somecycles = somecyclesBy (pure 0.5)

------

chooseWithSeed :: (Monad k) => Int -> [ZwirnT k st i a] -> ZwirnT k st i a
chooseWithSeed i ps = (ps !!) =<< shift (pure $ fromIntegral i / precision) (irand' $ pure $ length ps - 1)

chooseList :: (Monad k) => [ZwirnT k st i a] -> ZwirnT k st i a
chooseList = chooseWithSeed 0

enumFromToChoice :: (Ord a, Num a, Monad k) => Int -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a
enumFromToChoice i xz yz = join $ en <$> xz <*> yz
  where
    en x y = chooseWithSeed i $ map pure $ enumerateFromTo x y

enumFromThenToChoice :: (Ord a, Num a, Monad k) => Int -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a -> ZwirnT k st i a
enumFromThenToChoice i xz yz zz = join $ en <$> xz <*> yz <*> zz
  where
    en x y z = chooseWithSeed i $ map pure $ enumerateFromThenTo x y z

perlinSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double
perlinSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.perlin2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st

perlin :: (Applicative k) => ZwirnT k st i Double
perlin = perlinSeed (pure 0)

simplexSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double
simplexSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.openSimplex2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st

simplex :: (Applicative k) => ZwirnT k st i Double
simplex = simplexSeed (pure 0)

ssimplexSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double
ssimplexSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.superSimplex2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st

ssimplex :: (Applicative k) => ZwirnT k st i Double
ssimplex = ssimplexSeed (pure 0)

valueSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double
valueSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.value2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st

valueN :: (Applicative k) => ZwirnT k st i Double
valueN = valueSeed (pure 0)

cubicSeed :: (Applicative k) => ZwirnT k st i Int -> ZwirnT k st i Double
cubicSeed seed = zwirn $ \t st -> unzwirn ((\i -> (Noise.noise2At Noise.valueCubic2 (fromIntegral i) 0 (realToFrac t) + 1) / 2) <$> seed) t st

cubic :: (Applicative k) => ZwirnT k st i Double
cubic = cubicSeed (pure 0)