packages feed

tidal-1.10.3: src/Sound/Tidal/Transition.hs

{-# LANGUAGE BangPatterns #-}

module Sound.Tidal.Transition where

import Control.Concurrent.MVar (readMVar, swapMVar)
import qualified Data.Map.Strict as Map
-- import Data.Maybe (fromJust)

import qualified Sound.Tidal.Clock as Clock
import Sound.Tidal.Config (toClockConfig)
import Sound.Tidal.Control (_stut)
import Sound.Tidal.Core
import Sound.Tidal.ID (ID (fromID))
import Sound.Tidal.Params (gain, pan)
import Sound.Tidal.Pattern
import Sound.Tidal.Stream.Types
  ( PlayState (PlayState, psHistory, psMute, psPattern, psSolo),
    Stream (sClockRef, sConfig, sPMapMV),
  )
-- import Sound.Tidal.Tempo as T
import Sound.Tidal.UI (fadeInFrom, fadeOutFrom)
import Sound.Tidal.Utils (enumerate)
import Prelude hiding ((*>), (<*))

{-
    Transition.hs - A library for handling transitions between patterns
    Copyright (C) 2020, Alex McLean and contributors

    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/>.
-}

type TransitionMapper = Time -> [ControlPattern] -> ControlPattern

-- Evaluation of pat is forced so exceptions are picked up here, before replacing the existing pattern.
-- the "historyFlag" determines if the new pattern should be placed on the history stack or not
transition :: Stream -> Bool -> TransitionMapper -> ID -> ControlPattern -> IO ()
transition stream historyFlag mapper patId !pat = do
  let appendPat flag = if flag then (pat :) else id
      updatePS (Just playState) = playState {psHistory = appendPat historyFlag (psHistory playState)}
      updatePS Nothing =
        PlayState
          { psPattern = silence,
            psMute = False,
            psSolo = False,
            psHistory = appendPat historyFlag [silence]
          }
      transition' pat' = do
        t <- Clock.getCycleTime (toClockConfig $ sConfig stream) (sClockRef stream)
        return $! mapper t pat'
  pMap <- readMVar (sPMapMV stream)
  let playState = updatePS $ Map.lookup (fromID patId) pMap
  pat' <- transition' $ appendPat (not historyFlag) (psHistory playState)
  let pMap' = Map.insert (fromID patId) (playState {psPattern = pat'}) pMap
  _ <- swapMVar (sPMapMV stream) pMap'
  return ()

_mortalOverlay :: Time -> Time -> [Pattern a] -> Pattern a
_mortalOverlay _ _ [] = silence
_mortalOverlay t now (pat : ps) = overlay (pop ps) (playFor s (s + t) pat)
  where
    pop [] = silence
    pop (x : _) = x
    s = sam (now - fromIntegral (floor now `mod` floor t :: Int)) + sam t

-- | Washes away the current pattern after a certain delay by applying a
--    function to it over time, then switching over to the next pattern to
--    which another function is applied.
_wash :: (Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Time -> Time -> Time -> Time -> [Pattern a] -> Pattern a
_wash _ _ _ _ _ _ [] = silence
_wash _ _ _ _ _ _ [pat] = pat
_wash fout fin delay durin durout now (pat : pat' : _) =
  stack
    [ filterWhen (< (now + delay)) pat',
      filterWhen (between (now + delay) (now + delay + durin)) $ fout pat',
      filterWhen (between (now + delay + durin) (now + delay + durin + durout)) $ fin pat,
      filterWhen (>= (now + delay + durin + durout)) pat
    ]
  where
    between lo hi x = (x >= lo) && (x < hi)

_washIn :: (Pattern a -> Pattern a) -> Time -> Time -> [Pattern a] -> Pattern a
_washIn f durin now pats = _wash f id 0 durin 0 now pats

_xfadeIn :: Time -> Time -> [ControlPattern] -> ControlPattern
_xfadeIn _ _ [] = silence
_xfadeIn _ _ [pat] = pat
_xfadeIn t now (pat : pat' : _) = overlay (pat |* gain (now `rotR` _slow t envEqR)) (pat' |* gain (now `rotR` _slow t envEq))

-- | Pans the last n versions of the pattern across the field
_histpan :: Int -> Time -> [ControlPattern] -> ControlPattern
_histpan _ _ [] = silence
_histpan 0 _ _ = silence
_histpan n _ ps = stack $ map (\(i, pat) -> pat # pan (pure $ fromIntegral i / fromIntegral n')) (enumerate ps')
  where
    ps' = take n ps
    n' = length ps' -- in case there's fewer patterns than requested

-- | Just stop for a bit before playing new pattern
_wait :: Time -> Time -> [ControlPattern] -> ControlPattern
_wait _ _ [] = silence
_wait t now (pat : _) = filterWhen (>= nextSam (now + t - 1)) pat

-- | Just as `wait`, `waitT` stops for a bit and then applies the given transition to the playing pattern
--
-- @
-- d1 $ sound "bd"
--
-- t1 (waitT (xfadeIn 8) 4) $ sound "hh*8"
-- @
_waitT :: (Time -> [ControlPattern] -> ControlPattern) -> Time -> Time -> [ControlPattern] -> ControlPattern
_waitT _ _ _ [] = silence
_waitT f t now pats = filterWhen (>= nextSam (now + t - 1)) (f (now + t) pats)

-- |
-- Jumps directly into the given pattern, this is essentially the _no transition_-transition.
--
-- Variants of @jump@ provide more useful capabilities, see @jumpIn@ and @jumpMod@
_jump :: Time -> [ControlPattern] -> ControlPattern
_jump = _jumpIn 0

-- | Sharp `jump` transition after the specified number of cycles have passed.
--
-- @
-- t1 (jumpIn 2) $ sound "kick(3,8)"
-- @
_jumpIn :: Int -> Time -> [ControlPattern] -> ControlPattern
_jumpIn n = _wash id id (fromIntegral n) 0 0

-- | Unlike `jumpIn` the variant `jumpIn'` will only transition at cycle boundary (e.g. when the cycle count is an integer).
_jumpIn' :: Int -> Time -> [ControlPattern] -> ControlPattern
_jumpIn' n now = _wash id id (nextSam now - now + fromIntegral n) 0 0 now

-- | Sharp `jump` transition at next cycle boundary where cycle mod n == 0
_jumpMod :: Int -> Time -> [ControlPattern] -> ControlPattern
_jumpMod n now = _jumpIn' ((n - 1) - (floor now `mod` n)) now

-- | Sharp `jump` transition at next cycle boundary where cycle mod n == p
_jumpMod' :: Int -> Int -> Time -> [ControlPattern] -> ControlPattern
_jumpMod' n p now = _jumpIn' ((n - 1) - (floor now `mod` n) + p) now

-- | Degrade the new pattern over time until it ends in silence
_mortal :: Time -> Time -> Time -> [ControlPattern] -> ControlPattern
_mortal _ _ _ [] = silence
_mortal lifespan release now (p : _) = overlay (filterWhen (< (now + lifespan)) p) (filterWhen (>= (now + lifespan)) (fadeOutFrom (now + lifespan) release p))

_interpolate :: Time -> [ControlPattern] -> ControlPattern
_interpolate = _interpolateIn 4

_interpolateIn :: Time -> Time -> [ControlPattern] -> ControlPattern
_interpolateIn _ _ [] = silence
_interpolateIn _ _ [p] = p
_interpolateIn t now (pat : pat' : _) = f <$> pat' *> pat <* automation
  where
    automation = now `rotR` _slow t envL
    f a b x =
      Map.unionWith
        ( fNum2
            (\a' b' -> floor $ fromIntegral a' * x + fromIntegral b' * (1 - x))
            (\a' b' -> a' * x + b' * (1 - x))
        )
        b
        a

-- |
-- Degrades the current pattern while undegrading the next.
--
-- This is like @xfade@ but not by gain of samples but by randomly removing events from the current pattern and slowly adding back in missing events from the next one.
--
-- @
-- d1 $ sound "bd(3,8)"
--
-- t1 clutch $ sound "[hh*4, odx(3,8)]"
-- @
--
-- @clutch@ takes two cycles for the transition, essentially this is @clutchIn 2@.
_clutch :: Time -> [Pattern a] -> Pattern a
_clutch = _clutchIn 2

-- |
-- Also degrades the current pattern and undegrades the next.
-- To change the number of cycles the transition takes, you can use @clutchIn@ like so:
--
-- @
-- d1 $ sound "bd(5,8)"
--
-- t1 (clutchIn 8) $ sound "[hh*4, odx(3,8)]"
-- @
--
-- will take 8 cycles for the transition.
_clutchIn :: Time -> Time -> [Pattern a] -> Pattern a
_clutchIn _ _ [] = silence
_clutchIn _ _ [p] = p
_clutchIn t now (p : p' : _) = overlay (fadeOutFrom now t p') (fadeInFrom now t p)

-- | same as `anticipate` though it allows you to specify the number of cycles until dropping to the new pattern, e.g.:
--
-- @
-- d1 $ sound "jvbass(3,8)"
--
-- t1 (anticipateIn 4) $ sound "jvbass(5,8)"
-- @
_anticipateIn :: Time -> Time -> [ControlPattern] -> ControlPattern
_anticipateIn t now pats = _washIn (innerJoin . (\pat -> (\v -> _stut 8 0.2 v pat) <$> (now `rotR` _slow t (toRational <$> envLR)))) t now pats

-- wash :: (Pattern a -> Pattern a) -> (Pattern a -> Pattern a) -> Time -> Time -> Time -> Time -> [Pattern a] -> Pattern a

-- | `anticipate` is an increasing comb filter.
--
-- Build up some tension, culminating in a _drop_ to the new pattern after 8 cycles.
_anticipate :: Time -> [ControlPattern] -> ControlPattern
_anticipate = _anticipateIn 8

-- Deprecated aliases
mortalOverlay :: Time -> Time -> [Pattern a] -> Pattern a
mortalOverlay = _mortalOverlay

wash ::
  (Pattern a -> Pattern a) ->
  (Pattern a -> Pattern a) ->
  Time ->
  Time ->
  Time ->
  Time ->
  [Pattern a] ->
  Pattern a
wash = _wash

washIn ::
  (Pattern a -> Pattern a) ->
  Time ->
  Time ->
  [Pattern a] ->
  Pattern a
washIn = _washIn

xfadeIn :: Time -> Time -> [ControlPattern] -> ControlPattern
xfadeIn = _xfadeIn

histpan :: Int -> Time -> [ControlPattern] -> ControlPattern
histpan = _histpan

wait :: Time -> Time -> [ControlPattern] -> ControlPattern
wait = _wait

waitT ::
  (Time -> [ControlPattern] -> ControlPattern) ->
  Time ->
  Time ->
  [ControlPattern] ->
  ControlPattern
waitT = _waitT

jump :: Time -> [ControlPattern] -> ControlPattern
jump = _jump

jumpIn :: Int -> Time -> [ControlPattern] -> ControlPattern
jumpIn = _jumpIn

jumpIn' :: Int -> Time -> [ControlPattern] -> ControlPattern
jumpIn' = _jumpIn'

jumpMod :: Int -> Time -> [ControlPattern] -> ControlPattern
jumpMod = _jumpMod

jumpMod' :: Int -> Int -> Time -> [ControlPattern] -> ControlPattern
jumpMod' = _jumpMod'

mortal ::
  Time -> Time -> Time -> [ControlPattern] -> ControlPattern
mortal = _mortal

interpolate :: Time -> [ControlPattern] -> ControlPattern
interpolate = _interpolate

interpolateIn ::
  Time -> Time -> [ControlPattern] -> ControlPattern
interpolateIn = _interpolateIn

clutch :: Time -> [Pattern a] -> Pattern a
clutch = _clutch

clutchIn :: Time -> Time -> [Pattern a] -> Pattern a
clutchIn = _clutchIn

anticipateIn :: Time -> Time -> [ControlPattern] -> ControlPattern
anticipateIn = _anticipateIn

anticipate :: Time -> [ControlPattern] -> ControlPattern
anticipate = _anticipate