zwirn-0.2.2.1: src/zwirn-core/Zwirn/Core/Lib/Cord.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Zwirn.Core.Lib.Cord where
{-
Cord.hs - functions on parallel signals
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 Data.Bifunctor (first)
import Data.Foldable (Foldable (..), foldl')
import Zwirn.Core.Cord as C
import Zwirn.Core.Core
import Zwirn.Core.Lib.Conditional (iff)
import Zwirn.Core.Lib.Core
import Zwirn.Core.Lib.Modulate (bump, fastcat, fastcyclecatpat, shift)
import Zwirn.Core.Lib.Number
import Zwirn.Core.Time
import Zwirn.Core.Tree
import qualified Zwirn.Core.Tree as Tree
import Zwirn.Core.Types
import Prelude hiding (Foldable (..))
-- | get the current depth of the cord
depth :: Cord st i a -> Cord st i Int
depth = C.depth
superimpose :: Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a
superimpose f x = stack [apply f x, x]
ghostWith :: Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a
ghostWith t f x = stack [shift (t * 2.5) $ apply f x, shift (t * 1.5) $ apply f x, x]
-- | pick a certain layer out of a cord, wrapping around
pick :: Cord st i Int -> Cord st i a -> Cord st i a
pick iz x = (_pick <$> iz) `innerApply` x
inhabit :: Cord st i Int -> Cord st i a -> Cord st i a
inhabit iz xz = zwirn q
where
q t st = innerJoin $ (\v -> look (value v) $ unzwirn xz (time v) st) . fst <$> ik
where
ik = unzwirn iz t st
-- | pick a certain layer out of a cord, silence when out of bounds
pick' :: Cord st i Int -> Cord st i a -> Cord st i a
pick' i x = (withInner . look' <$> i) `innerApply` x
-- | pick a certain layer out of a cord, wrapping around
select :: Cord st i Double -> Cord st i a -> Cord st i a
select d x = Zwirn.Core.Lib.Cord.pick i x
where
i = join $ liftA2 (\l k -> if k > 0 && abs l <= 1 then pure $ Prelude.floor $ l * fromIntegral k else silence) d (C.depth x)
-- | insert cord a specific index
insert :: Cord st i Int -> Cord st i a -> Cord st i a -> Cord st i a
insert ic x y = flip ($ x) y . insert' =<< ic
where
insert' :: Int -> Cord st i a -> Cord st i a -> Cord st i a
insert' i z ys = zwirn $ \t st -> insertT i (unzwirn z t st) (unzwirn ys t st)
-- | push to the top of the cord
push :: Cord st i a -> Cord st i a -> Cord st i a
push = withInner2 Tree.push
concat :: Cord st i a -> Cord st i a -> Cord st i a
concat = withInner2 Tree.concat
-- | remove the top of the cord
pop :: Cord st i a -> Cord st i a
pop = withInner Tree.pop
-- | remove cord at specific index
remove :: Cord st i Int -> Cord st i a -> Cord st i a
remove i x = (withInner . removeT <$> i) `innerApply` x
-- | apply function to specific index
at :: Cord st i Int -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a
at i f x = insert i (innerApply f $ pick i x) (remove i x)
applyCord :: (Num a) => Cord st i a -> Cord st i a -> Cord st i a
applyCord = liftA2Left (+)
reverse :: Cord st i a -> Cord st i a
reverse = liftList Prelude.reverse
rotate :: Cord st i Int -> Cord st i a -> Cord st i a
rotate iz xz = flip liftList xz . rotateList =<< iz
where
rotateList n xs
| n > 0 = Prelude.take (length xs) (Prelude.drop n (cycle $ Prelude.reverse xs))
| otherwise = Prelude.take (length xs) (Prelude.drop n (cycle xs))
take :: Cord st i Int -> Cord st i a -> Cord st i a
take iz xz = flip liftList xz . Prelude.take =<< iz
drop :: Cord st i Int -> Cord st i a -> Cord st i a
drop iz xz = flip liftList xz . Prelude.drop =<< iz
filter :: Cord st i (Cord st i a -> Cord st i Bool) -> Cord st i a -> Cord st i a
filter fz xz = iff bz xz
where
bz = cordmap fz xz
invert :: (Num a) => Cord st i a -> Cord st i a
invert = liftList invertList
where
invertList [] = []
invertList (x : xs) = xs ++ [fmap (first $ fmap (+ 12)) x]
open :: (Num a) => Cord st i a -> Cord st i a
open = liftList openList
where
openList ds = case ds of
(d : _ : _ : _) -> [fmap (first $ fmap (+ (-12))) d, fmap (first $ fmap (+ (-12))) (ds !! 2), ds !! 1] ++ Prelude.reverse (Prelude.take (length ds - 3) (Prelude.reverse ds))
_ -> ds
expand :: (Num a) => Cord st i Int -> Cord st i a -> Cord st i a
expand iz xz = flip liftList xz . expandList =<< iz
where
expandList i ds = Prelude.take i $ concatMap (\x -> map (fmap $ first $ fmap (+ fromIntegral x)) ds) [0 :: Int, 12 ..]
enumFromToStack :: (Ord a, Num a) => Cord st i a -> Cord st i a -> Cord st i a
enumFromToStack xz yz = join $ en <$> xz <*> yz
where
en x y = stack $ map pure $ enumerateFromTo x y
enumFromThenToStack :: (Ord a, Num a) => Cord st i a -> Cord st i a -> Cord st i a -> Cord st i a
enumFromThenToStack xz yz zz = join $ en <$> xz <*> yz <*> zz
where
en x y z = stack $ map pure $ enumerateFromThenTo x y z
replicate :: Cord st i Int -> Cord st i a -> Cord st i a
replicate i x = (stack . flip Prelude.replicate x) =<< i
fold :: Cord st i (Cord st i a -> Cord st i (Cord st i a -> Cord st i a)) -> Cord st i a -> Cord st i a
fold fz xz = fold' fz =<< collect xz
where
fold' _ [] = silence
fold' _ [x] = x
fold' f (x : xs) = foldl' (\a b -> f `apply` a `apply` b) x xs
cordcat :: Cord st i a -> Cord st i a
cordcat x = fastcat =<< collect x
timerun :: Cord st i Time -> Cord st i Int
timerun tz = (fastcyclecatpat . flip zip (map pure [0 :: Int ..])) =<< collect tz
interpol :: Cord st i Time -> Cord st i Time
interpol x = _interpol =<< collect x
cordmap :: Cord st i (Cord st i a -> Cord st i b) -> Cord st i a -> Cord st i b
cordmap f x = (stack . map (apply f)) =<< collect x
layer :: Cord st i (Cord st i a -> Cord st i b) -> Cord st i a -> Cord st i b
layer f x = (stack . map (`apply` x)) =<< collect f
arp :: Cord st i a -> Cord st i a
arp x = apply (squeezeMap (pure . bump) ds) x
where
d = C.depth x
ds = enumFromThenToStack @Time 0 ((\dp -> if dp == 0 then 0 else 1 / fromIntegral dp) <$> d) 1
echoWith :: Cord st i Int -> Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a
echoWith cz tz fz xz = echoWith' =<< cz
where
echoWith' c = if c < 1 then silence else stack $ scanl (\ !prev _ -> apply fz $ shift tz prev) xz [0 .. c - 1]
followWith :: Cord st i Int -> Cord st i Time -> Cord st i (Cord st i a -> Cord st i a) -> Cord st i a -> Cord st i a
followWith cz tz fz xz = followWith' =<< cz
where
followWith' c = if c < 1 then silence else stack $ scanl (\ !prev _ -> apply fz $ bump tz prev) xz [0 .. c - 1]