{-| Module : Bang.Music.Transform Description : General transformations on compositions Copyright : (c) Benjamin Kovach, 2014 License : MIT Maintainer : bkovach13@gmail.com Stability : experimental Portability : Mac OSX This module exports a number of functions to manipulate compositions in various ways. -} module Bang.Music.Transform where import Bang.Music.Class import Bang.Interface.Base import Data.Monoid import Data.Bifunctor import Data.Foldable(foldMap) -- |Reverses a composition reverseMusic :: Music Dur b -> Music Dur b reverseMusic p@(Prim _) = p reverseMusic (a :+: b) = reverseMusic b :+: reverseMusic a reverseMusic (a :=: b) | durA < durB = (rest diff :+: reverseMusic a) :=: reverseMusic b | durB < durA = reverseMusic a :=: (rest diff :+: reverseMusic b) | otherwise = reverseMusic a :=: reverseMusic b where (durA, durB) = (duration a, duration b) diff = abs $ durA - durB reverseMusic m@(Modify c a) = Modify c (reverseMusic a) -- |Play a composition forwards, then backwards. mirror :: Music Dur b -> Music Dur b mirror m = m <> reverseMusic m -- |Play a composition backwards, then forwards. mirrorR :: Music Dur b -> Music Dur b mirrorR m = reverseMusic m <> m -- |Play a composition forwards and backwards concurrently. cross :: Music Dur b -> Music Dur b cross m = m `cappend` reverseMusic m -- |Take the first `d` duration units of a composition. takeDur :: Dur -> Music Dur b -> Music Dur b takeDur = go where go dr m | dr <= 0 = rest (abs dr) | otherwise = case m of p@(Prim _) -> p (a :+: b) -> go dr a :+: go (dr - duration a) b (a :=: b) -> go dr a :=: go dr b (Modify c a) -> Modify c (go dr a) -- |Drop the first `d` duration units of a composition. dropDur :: Dur -> Music Dur b -> Music Dur b dropDur = go where go dr m | dr <= 0 = m | otherwise = case m of p@(Prim (Rest d')) -> rest 0 p@(Prim (Note d' _)) -> rest 0 (a :+: b) -> go dr a :+: go (dr - duration a) b (a :=: b) -> go dr a :=: go dr b (Modify c a) -> Modify c (go dr a) -- |Split a composition at a specific duration and return the composition -- before said duration along with the rest of it. partitionDur :: Dur -> Music Dur b -> (Music Dur b, Music Dur b) partitionDur d m = (takeDur d m, dropDur d m) -- |Turn the first `d` duration units of a composition into silence. hushFor :: Dur -> Music Dur b -> Music Dur b hushFor d m = rest d <> dropDur d m -- |Turn the rest of a composition into silence after `d` duration units. hushFrom :: Dur -> Music Dur b -> Music Dur b hushFrom d m = takeDur d m <> rest (max (duration m - d) 0) -- |Turn the section of a composition between `pos` and `d` into silence. hushAt :: Dur -> Dur -> Music Dur b -> Music Dur b hushAt pos d m = pre <> rest d <> dropDur d post where (pre, post) = partitionDur pos m -- |Play a polyrhythm with 'm' having units of length 1\/x and 'n' with units of length 1\/y -- -- Example: -- -- > poly (3, 3 #> bd) (4, 4 #> sn) poly :: (Dur, Music Dur b) -> (Dur, Music Dur b) -> Music Dur b poly (x, m) (y, n) = tempo (x/4) m :=: tempo (y/4) n -- |Set the duration of a composition withDuration :: Dur -> Music Dur b -> Music Dur b withDuration d m = first (*(d/d')) m where d' = duration m -- |Replicate a composition `n` times. repl :: Num a => Int -> Music a b -> Music a b repl n = mconcat . replicate n -- |Infinitely repeat a composition. rep :: Num a => Music a b -> Music a b rep = mconcat . repeat -- |Fit the duration of `b` to the duration of `a` fitL :: Music Dur b -> Music Dur b -> Music Dur b fitL a = cappend a . withDuration (duration a) -- |Fit the duration of `a` into the duration of `b` fitR :: Music Dur b -> Music Dur b -> Music Dur b fitR = flip fitL -- |Normalize the durations of each value in a list of Compositions to `d` and compose them sequentially. normalize :: Dur -> [Music Dur b] -> Music Dur b normalize d = foldMap (withDuration d) -- |Normalize the durations of each value in a list of Compositions to `d` and compose them concurrently. normalizeC :: Dur -> [Music Dur b] -> Music Dur b normalizeC d = cconcat . map (withDuration d) -- |Normalize each composition's duration to the duration of the first element in the list and compose sequentially. normalize1 :: [Music Dur b] -> Music Dur b normalize1 (x:xs)= foldMap (withDuration (duration x)) (x:xs) -- |Normalize each composition's duration to the duration of the first element in the list and compose concurrently. normalizeC1 :: [Music Dur b] -> Music Dur b normalizeC1 (x:xs) = cconcat $ map (withDuration (duration x)) (x:xs)