packages feed

zwirn-core-0.1.1.0: src/Zwirn/Core/Modulate.hs

{-# LANGUAGE BangPatterns #-}

module Zwirn.Core.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 Control.Applicative
import Control.Monad (join)
import Control.Monad.Identity
import Data.Bifunctor
import Data.Fixed (mod')
import Data.Functor (void)
import Music.Theory.Bjorklund (bjorklund, iseq)
import Zwirn.Core.Core
import Zwirn.Core.Time
import Zwirn.Core.Tree
import Zwirn.Core.Types

modulateTime :: (a -> Time -> st -> Time) -> a -> ZwirnT k st i b -> ZwirnT k st i b
modulateTime f x b = zwirn (\t st -> unzwirn b (f x t st) st)

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 (\x t _ -> fromIntegral (floor x) + t - frac x) <$> tz <$$> 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 (\x t _ -> t * x) <$> tz <$$> 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 <$$> x
  where
    timefunc x t _
      | x == 0 = 0
      | otherwise = t / x

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

ply :: (MultiMonad k) => ZwirnT k st i Time -> ZwirnT k st i a -> ZwirnT k st i a
ply tz = squeezeMap (fast tz)

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 <$$> x
  where
    tup = liftA2 (,) t1 t2
    timefunc (st, en) t _
      | en > st = mod' t (en - st) + st
      | en == st = 0
      | en < st = st - mod' t (st - en)

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

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

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
        cyc = floor t
        phase = frac scaledPhase + fromIntegral cyc

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, 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 [(_, p)] _ = p
    cyclecatrec (x : xs) !tot = cat x (tot - fst x, cyclecatrec xs (tot - fst x))

cat :: (HasSilence k) => (Time, ZwirnT k st i a) -> (Time, ZwirnT k st i a) -> ZwirnT k st i a
cat (Time 0 _, _) (Time 0 _, _) = silence
cat (t1, p1) (t2, p2) = zwirn q
  where
    q t = unzwirn item phase
      where
        total = t1 + t2
        cyc = t / total
        first = frac cyc < t1 / total
        item = if first then p1 else p2
        phase = if first then t - fromIntegral (floor cyc) * t2 else t - (fromIntegral (floor cyc) + 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