{-# LANGUAGE FlexibleContexts #-}
-- | Functions for score composition.
--
--
module Temporal.Music.Notation.Score (
-- * Types
Time, Dur, Score,
-- * Constructors
rest, note,
-- * Duration querry
dur,
-- * Composition
(+:+), (=:=), (=:/),
line, chord, chordT,
loop, trill,
-- * Transformers
-- ** In time domain
delay,
stretch,
bpm, dot, ddot, tri,
slice, takeS, dropS, reverseS,
pedal, pedalBy,
sustain, sustainBy,
-- ** Mappings
tmap, dmap, tdmap,
-- * Rendering
renderScore,
-- * Miscellaneous
tmapRel, dmapRel, tdmapRel,
linseg
)
where
import qualified Temporal.Media as M
import Temporal.Media(linseg)
import Control.Arrow(first, second)
-- | time
type Time = Double
-- | duration
type Dur = Double
-- | In 'Score' @a@ values of type @a@
-- can be wrapped in time events as if they present or abscent for some
-- time 'Dur' and combined together in parrallel or sequent ways.
--
-- Score is instance of
--
-- * 'Functor' 'Score'
--
-- 'Functor' instance means that you can map over score values
-- with some function @(a -> b)@, rests are mapped to rests and values
-- transformed with given function.
type Score a = M.Media Dur a
-- | querry score's duration
dur :: Score a -> Dur
dur = M.dur
-- | pause for some "Dur" time
rest :: Dur -> Score a
rest = M.none
-- | stretch in time domain. Duration of every note segemnt is multiplied by
-- given factor.
stretch :: Dur -> Score a -> Score a
stretch = M.stretch
-- | stretch with 1.5
dot :: Score a -> Score a
dot = stretch 1.5
-- | double 'dot', stretch with 1.75
ddot :: Score a -> Score a
ddot = stretch 1.75
-- | stretch with 2/3
tri :: Score a -> Score a
tri = stretch (2/3)
-- | adds given amount of duration to all notes
sustain :: Dur -> Score a -> Score a
sustain k = sustainBy $ \t d a -> (d + k, a)
-- | set tempo in beats per minute,
-- if 1 "Dur" is equal to 1 second before transformation.
bpm :: Double -> (Score a -> Score a)
bpm beat = stretch (x1/x0)
where x0 = 0.25
x1 = 60/beat
-- | general sustain
sustainBy :: (Time -> Dur -> a -> (Dur, b)) -> Score a -> Score b
sustainBy f = M.eventMap $
\(M.Event t d a) -> let (d', a') = f t d a
in M.Event t d' a'
-- | adds sustain, but total duration of score elements remains unchaged
--
-- notes are sustained within total duration interval.
-- adds given amount of time to all notes.
pedal :: Dur -> Score a -> Score a
pedal dt' = pedalBy (\t dt a -> (dt + dt', a))
-- | general \"pedal\"
--
-- Total duration of score element remains unchanged. notes are sustained within total duration interval
pedalBy :: (Time -> Dur -> a -> (Dur, b)) -> Score a -> Score b
pedalBy f x = sustainBy f' x
where d = dur x
f' t dt a = first (min (d - t)) $ f t dt a
-- | Constructor of score. Constructs note out of given value that lasts
-- for some time.
note :: Dur -> a -> Score a
note = M.temp
-- | Delay scores by given duration.
delay :: Dur -> Score a -> Score a
delay = M.delay
-- | binary sequential composition, @a +:+ b@ means play a and then play b.
(+:+) :: Score a -> Score a -> Score a
(+:+) = (M.+:+)
-- | binary parallel composition, @a =:= b@ means play a and b simultoneously.
(=:=) :: Score a -> Score a -> Score a
(=:=) = (M.=:=)
-- | turncating parallel composition
--
-- for a =:/ b composes two scores together and turncates biggest one by
-- duration of smallest one.
(=:/) :: Score a -> Score a -> Score a
a =:/ b
| dur a < dur b = a =:= takeS (dur a) b
| otherwise = b =:= takeS (dur b) a
-- | sequential composition for list of scores
line :: [Score a] -> Score a
line = M.sequent
-- | parallel composition for list of scores
chord :: [Score a] -> Score a
chord = M.parallel
-- | turncating parallel composition for lists of scores
chordT :: [Score a] -> Score a
chordT xs = chord $ map (takeS d) xs
where d = minimum $ map dur xs
-- | Arranges n copies of score in line.
loop :: Int -> Score a -> Score a
loop = M.loop
-- | loop for two groups of notes. Repeats n times line of two scores.
trill :: Int -> Score a -> Score a -> Score a
trill n a b = loop n $ line [a, b]
-- | extracting score parts in some time interval.
-- it reverses output if @t1 < t0@.
slice :: Dur -> Dur -> Score a -> Score a
slice = M.slice
-- | take sub-score from begining
takeS :: Dur -> Score a -> Score a
takeS = M.takeM
-- | drop sub-score
dropS :: Dur -> Score a -> Score a
dropS = M.dropM
-- | reverse score
reverseS :: Score a -> Score a
reverseS = M.reverseM
-- | temporal functor 'tmap' method for scores
--
-- map with time
tmap :: (Time -> a -> b) -> Score a -> Score b
tmap = M.tmap
-- | temporal functor 'dmap' method for scores
--
-- map with duration
dmap :: (Dur -> a -> b) -> Score a -> Score b
dmap = M.dmap
-- | temporal functor 'tdmap' method for scores
--
-- map with time and duration
tdmap :: (Time -> Dur -> a -> b) -> Score a -> Score b
tdmap = M.tdmap
-- | relative 'tmap'
--
-- map with time normalized by total duration value
tmapRel :: (Time -> a -> b) -> Score a -> Score b
tmapRel = M.tmapRel
-- | relative 'dmap'
--
-- map with duration normalized by total duration value
dmapRel :: (Dur -> a -> b) -> Score a -> Score b
dmapRel = M.dmapRel
-- | relative 'tdmap'
--
-- map with time and duration normalized by total duration value
tdmapRel :: (Time -> Dur -> a -> b) -> Score a -> Score b
tdmapRel = M.tdmapRel
-------------------------------------------------------
-- Rendering
-- | Transform 'Score' to 'EventList'
renderScore :: Score a -> M.EventList Dur a
renderScore = M.renderMedia