----------------------------------------------------------------------------- -- | -- 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