music-score-1.7.2: src/Music/Time/Juxtapose.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-------------------------------------------------------------------------------------
-- |
-- Copyright : (c) Hans Hoglund 2012-2014
--
-- License : BSD-style
--
-- Maintainer : hans@hanshoglund.se
-- Stability : experimental
-- Portability : non-portable (TF,GNTD)
--
-------------------------------------------------------------------------------------
module Music.Time.Juxtapose (
-- * Align without composition
lead,
follow,
-- * Standard composition
after,
before,
during,
(|>),
(<|),
-- ** More exotic
sustain,
palindrome,
-- * Catenation
scat,
pcat,
-- * Repetition
times,
) where
import Control.Lens hiding ((|>), (<|))
import Data.AffineSpace
import Data.AffineSpace.Point
-- import Data.Monoid.WithSemigroup
import Data.Semigroup
import Data.VectorSpace
import Music.Time.Reverse
import Music.Time.Split
--
-- TODO names
-- Especially 'after' is counter-intuitive
--
-- |
-- Move a value so that
--
-- @
-- '_offset' (a `lead` b) = '_onset' b
-- @
--
--
lead :: (HasPosition a, HasPosition b, Transformable a) => a -> b -> a
a `lead` b = placeAt 1 (b `_position` 0) a
-- |
-- Move a value so that
--
-- @
-- '_offset' a = '_onset' (a `follow` b)
-- @
--
follow :: (HasPosition a, HasPosition b, Transformable b) => a -> b -> b
a `follow` b = placeAt 0 (a `_position` 1) b
-- |
-- Move a value so that
--
after :: (Semigroup a, Transformable a, HasPosition a) => a -> a -> a
a `after` b = a <> (a `follow` b)
-- |
-- Move a value so that
--
before :: (Semigroup a, Transformable a, HasPosition a) => a -> a -> a
a `before` b = (a `lead` b) <> b
-- |
-- A value followed by its reverse (retrograde).
--
palindrome :: (Semigroup a, Reversible a, HasPosition a) => a -> a
palindrome a = a `after` rev a
infixr 6 |>
infixr 6 <|
-- |
-- An infix alias for 'after'.
--
(|>) :: (Semigroup a, HasPosition a, Transformable a) => a -> a -> a
(|>) = after
-- |
-- An infix alias for 'before'.
--
(<|) :: (Semigroup a, HasPosition a, Transformable a) => a -> a -> a
(<|) = before
-- infixr 6 >|
-- infixr 6 |<
-- |
-- Compose a list of sequential objects, with onset and offset tangent to one another.
--
-- For non-positioned types, this is the often same as 'mconcat'
-- For positioned types, this is the same as 'afterAnother'
--
scat :: (Semigroup a, Monoid a, HasPosition a, Transformable a) => [a] -> a
scat = Prelude.foldr (|>) mempty
-- |
-- Compose a list of parallel objects, so that their local origins align.
--
-- This not possible for non-positioned types, as they have no notion of an origin.
-- For positioned types this is the same as 'mconcat'.
--
pcat :: (Semigroup a, Monoid a) => [a] -> a
pcat = Prelude.foldr (<>) mempty
-- |
-- Move a value so that its era is equal to the era of another value.
--
-- @
-- 'Score' a -> 'Score' a -> 'Score' a
-- @
--
during :: (HasPosition a, HasPosition b, Transformable a, Transformable b) => a -> b -> a
y `during` x = set era (view era x) y
-- |
-- Like '<>', but scaling the second agument to the duration of the first.
--
-- @
-- 'Score' a -> 'Score' a -> 'Score' a
-- @
--
sustain :: (Semigroup a, HasPosition a, Transformable a) => a -> a -> a
x `sustain` y = x <> y `during` x
-- |
-- Repeat exact amount of times.
--
-- @
-- 'Int' -> 'Score' a -> 'Score' a
-- @
--
times :: (Semigroup a, Monoid a, HasPosition a, Transformable a) => Int -> a -> a
times n = scat . replicate n