packages feed

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

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TypeApplications #-}

module Zwirn.Core.Lib.Modulate where

{-
    Modulate.hs - functions modulating time
    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 Data.Fixed (mod')
import Zwirn.Core.Core
import Zwirn.Core.Lib.Core
import Zwirn.Core.Time
import Zwirn.Core.Tree
import Zwirn.Core.Types

rev :: ZwirnT k st i a -> ZwirnT k st i a
rev = modulateTime (\_ t _ -> -t) ()

revBy :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
revBy tz x = (modulateTime (\y t _ -> fromIntegral @Int (floor y) + t - frac y) <$> tz) `innerApply` x

sini :: ZwirnT k st i a -> ZwirnT k st i a
sini = modulateTime (\_ t _ -> sin (2 * pi * t)) ()

fast :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
fast tz x = (modulateTime (\y t _ -> t * y) <$> tz) `innerApply` x

slow :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
slow tz x = (modulateTime timefunc <$> tz) `innerApply` x
  where
    timefunc y t _
      | y == 0 = 0
      | otherwise = t / y

shift :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
shift tz x = (modulateTime (\y t _ -> t - y) <$> tz) `innerApply` x

ply :: (MultiMonad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
ply tz = apply (matchApply ply' tz)
  where
    ply' = pure $ \t -> pure $ \x -> squeezeMap (fast t) x

bump :: (MultiMonad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
bump tz = apply (matchApply bump' tz)
  where
    bump' = pure $ \t -> pure $ \x -> squeezeMap (shift t) x

-- zoom into a specific region of time, while preserving the speed
zoom :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
zoom t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x
  where
    tup = liftA2 (,) t1 t2
    timefunc (st, en) t _
      | en > st = mod' (t * (en - st)) (en - st) + st
      | en == st = st
      | otherwise = st - mod' (t * (st - en)) (st - en)

-- loop a specific region of time, the shorter the region, the faster it gets
loop :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
loop t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x
  where
    tup = liftA2 (,) t1 t2
    timefunc (st, en) t _
      | en > st = mod' t (en - st) + st
      | en == st = st
      | otherwise = st - mod' t (st - en)

timeloop :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
timeloop = loop (pure 0)

loopfirst :: (Monad k) => ZwirnT k st i a -> ZwirnT k st i a
loopfirst = timeloop (pure 1)

ribbon :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
ribbon off len = loop off (liftA2 (+) off len)

swingBy :: (Monad k) => ZwirnT k st i Time -> ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
swingBy t1 t2 x = (modulateTime timefunc <$> tup) `innerApply` x
  where
    tup = liftA2 (,) t1 t2
    timefunc (sh, segs) t _
      | odd (floor $ frac t * segs * 2 :: Int) = t - (sh / (segs * 2))
      | segs == 0 = 0
      | otherwise = t

----------------------------------------------
------------------ cats ----------------------
----------------------------------------------

fastcat :: (HasSilence k) => [ZwirnT k st i a] -> ZwirnT k st i a
fastcat [] = silence
fastcat obj = zwirn q
  where
    q t = unzwirn item phase
      where
        metre = fromIntegral $ length obj
        scaledPhase = t * metre
        item = nth scaledPhase obj
        cy = floor t
        phase = frac scaledPhase + fromIntegral @Int cy

slowcat :: (HasSilence k, Monad k) => [ZwirnT k st i a] -> ZwirnT k st i a
slowcat zs = slow (pure $ fromIntegral $ length zs) $ fastcat zs

-- | each (t,p) indicates the amount of time t for pattern p relative
-- | to the other lengths in the list, squeezed within one cycle
timecat :: (HasSilence k, Monad k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a
timecat tps = if total == 0 then silence else cyclecat normalised
  where
    total = sum $ map fst tps
    normalised = map (\(t, p) -> (t / total, slow (pure $ t / total) p)) tps

-- | each (t,p) indicates the amount of time t the pattern p is queried for
-- | the patterns in the list will be queried in order by their respective amounts
-- | Example: cyclecat [(1,pure 10), (2, slow 2 $ pure 20)] == < 10 20 ~ >
-- | Note: also works with rational numbers
cyclecat :: (HasSilence k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a
cyclecat [] = silence
cyclecat xs = cyclecatrec xs (sum $ map fst xs)
  where
    -- len = sum $ map fst xs
    cyclecatrec [] _ = silence
    cyclecatrec [(_, p)] _ = p
    cyclecatrec (x : ys) !tot = cat x (tot - fst x, cyclecatrec ys (tot - fst x))

cat :: (HasSilence k) => (Time, ZwirnT k st i a) -> (Time, ZwirnT k st i a) -> ZwirnT k st i a
cat (t1, p1) (t2, p2) = if total == 0 then silence else zwirn q
  where
    total = t1 + t2
    q t = unzwirn item phase
      where
        cy = t / total
        first = frac cy < t1 / total
        item = if first then p1 else p2
        phase = if first then t - fromIntegral @Int (floor cy) * t2 else t - (fromIntegral @Int (floor cy) + 1) * t1

fastcyclecat :: (HasSilence k, Monad k) => [(Time, ZwirnT k st i a)] -> ZwirnT k st i a
fastcyclecat xs = cyclecat $ map (\(t, x) -> (t, slow (pure t) x)) xs

catpat :: (MultiMonad k, HasSilence k) => (ZwirnT k st i Time, ZwirnT k st i a) -> (ZwirnT k st i Time, ZwirnT k st i a) -> ZwirnT k st i a
catpat (t1z, p1) (t2z, p2) = innerJoin $ liftA2 (\t1 t2 -> cat (t1, p1) (t2, p2)) t1z t2z

cyclecatpat :: (MultiMonad k, HasSilence k) => [(ZwirnT k st i Time, ZwirnT k st i a)] -> ZwirnT k st i a
cyclecatpat [] = silence
cyclecatpat xs = cyclecatrec xs (foldl' (liftA2 (+)) (pure 0) $ map fst xs)
  where
    cyclecatrec [] _ = silence
    cyclecatrec [(_, p)] _ = p
    cyclecatrec (x : ys) !tot = catpat x (newTot, cyclecatrec ys newTot)
      where
        newTot = liftA2 (-) tot (fst x)

fastcyclecatpat :: (MultiMonad k, HasSilence k) => [(ZwirnT k st i Time, ZwirnT k st i a)] -> ZwirnT k st i a
fastcyclecatpat xs = cyclecatpat $ map (\(t, x) -> (t, slow t x)) xs