mezzo-0.3.1.0: src/Mezzo/Render/Transform.hs
-----------------------------------------------------------------------------
-- |
-- Module : Mezzo.Render.Transform
-- Description : Score-level transformations
-- Copyright : (c) Dima Szamozvancev
-- License : MIT
--
-- Maintainer : ds709@cam.ac.uk
-- Stability : experimental
-- Portability : portable
--
-- Functions and combinators for transforming Scores. Allows for more flexibility,
-- but no static correctness guarantees.
--
-----------------------------------------------------------------------------
module Mezzo.Render.Transform
(transpose, delay, (><), (+++), flatten, reprise, cascade, scale, volta)
where
import Mezzo.Render.Score
import Mezzo.Render.MIDI
import Codec.Midi hiding (key, Key)
import qualified Codec.Midi as CM (key, Key)
import Prelude hiding (min)
import Control.Arrow (first, second)
-- | Transpose a single MIDI message by the given number of semitones.
transposeMessage :: Int -> Message -> Message
transposeMessage t (NoteOff ch key vel) = NoteOff ch (key + t) vel
transposeMessage t (NoteOn ch key vel) = NoteOn ch (key + t) vel
transposeMessage _ m = m
-- | Transpose a score by the given number of semitones.
transpose :: Int -> Score -> Score
transpose n = map (second (transposeMessage n))
-- | Delay a score by the given number of ticks (60ths of a thirty-second note).
delay :: Ticks -> Score -> Score
delay ticks (n@(_, NoteOn{}) : rest) =
[ (0, NoteOn {channel = 0, CM.key = 60, velocity = 0})
, (ticks, NoteOn {channel = 0, CM.key = 60, velocity = 0})
] ++ n : rest
delay ticks (m : rest) = m : delay ticks rest
-- | Remove the attribute MIDI messages in the score header.
stripHeader :: Score -> Score
stripHeader [] = []
stripHeader (n@(_, NoteOn{}) : rest) = n : rest
stripHeader (_ : rest) = stripHeader rest
-- | Concatenation of scores.
(+++) :: Score -> Score -> Score
s1 +++ s2 = s1 ++ stripHeader s2
-- | Flatten a list of scores into a single score.
flatten :: [Score] -> Score
flatten = foldr (+++) []
-- | Repeat a score the given number of times.
reprise :: Int -> Score -> Score
reprise n = flatten . replicate n
-- | Repeat the score, successively applying the function to each repetition.
-- cascade 4 f s = s +++ f s +++ f (f s) +++ f (f (f s))
cascade :: Int -> (Score -> Score) -> Score -> Score
cascade n f = flatten . take n . iterate f
-- | Create a scale of the given length and interval between scores.
scale :: Int -> Int -> Score -> Score
scale l i = cascade l (transpose i)
-- | Repeat the score with the different endings at each repetition.
volta :: Score -> [Score] -> Score
volta s vs = flatten $ map (s +++) vs