
\subsection{Optimization}
\seclabel{optimization}

This module provides functions that simplify the structure
of a \code{Music.T} according to the rules proven in
\secref{equivalence}

\begin{haskelllisting}

> module Haskore.Process.Optimization where

> import qualified Medium.Controlled.List as CtrlMediumList
> import qualified Medium.Controlled      as CtrlMedium
> import qualified Haskore.Music as Music
> import Medium.Controlled.List (serial, parallel)
> import Haskore.General.Utility (toMaybe, partitionMaybe)
> import Data.Maybe (catMaybes, fromMaybe)

\end{haskelllisting}

\code{Music.T} objects that come out of \code{ReadMidi.toMusic}
almost always contain redundancies,
like rests of zero duration and redundant instrument specifications.
The function \function{Optimization.all}
reduces the redundancy to make a \type{Music.T} file
less cluttered and more efficient to use.
\begin{haskelllisting}

> all, rest, composition, duration, tempo, transpose, volume ::
>    Music.T note -> Music.T note
> all = tempo . transpose . volume . singleton . composition . rest

\end{haskelllisting}

Remove rests of zero duration.
\begin{haskelllisting}

> rest = Music.mapList
>    (,)
>    (flip const)
>    (filter (not . isZeroRest))
>    (filter (not . isZeroRest))

> isZeroRest :: Music.T note -> Bool
> isZeroRest =
>    Music.switchList
>       (\d at -> d==0 && maybe True (const False) at)
>       (const (const False))
>       (const False)
>       (const False)

\end{haskelllisting}

Remove empty parallel and serial compositions
and controllers of empty music.
\begin{haskelllisting}

> composition = fromMaybe (Music.rest 0) . Music.foldList
>    (\d -> Just . Music.atom d)
>    (fmap . Music.control)
>    ((\ms -> toMaybe (not (null ms)) (serial   ms)) . catMaybes)
>    ((\ms -> toMaybe (not (null ms)) (parallel ms)) . catMaybes)

\end{haskelllisting}

Remove any atom of zero duration.
This is not really an optimization but a hack to get rid
of MIDI NoteOn and NoteOff events at the same time point.
\begin{haskelllisting}

> duration = fromMaybe (Music.rest 0) . Music.foldList
>    (\d -> toMaybe (d /= 0) . Music.atom d)
>    (fmap . Music.control)
>    (Just . serial   . catMaybes)
>    (Just . parallel . catMaybes)

\end{haskelllisting}

The control structures for tempo, transposition and change of instruments
can be handled very similar using the following routines.
The function \function{mergeControl'} checks
if nested controllers are of the same kind.
If they are then they are merged into one.
The function would be much simpler
if it would be implemented for specific constructors,
but we want to stay independent from the particular data structure,
which is already quite complex.
\begin{haskelllisting}

> mergeControl' ::
>       (Music.Control -> Maybe a)
>    -> (a -> Music.T note -> Music.T note)
>    -> (a -> a -> a)
>    -> Music.T note
>    -> Music.T note
> mergeControl' extract control merge =
>    let fcSub c m = fmap (flip (,) m) (extract c)
>        fc' c0 m0 x0 =
>                 maybe (Music.control c0 m0)
>                       (\(x1,m1) -> control (merge x0 x1) m1)
>                       (Music.switchList (const (const Nothing))
>                           fcSub (const Nothing) (const Nothing) m0)
>        fc c m = maybe (Music.control c m)
>                       (fc' c m)
>                       (extract c)
>    in  Music.foldList
>           Music.atom fc Music.line Music.chord

\end{haskelllisting}

The following function collects neighboured controllers into groups,
extracts controllers of a specific type
and prepends a controller to the list of neighboured controllers,
which has the total effect of the extracted controllers.
This change of ordering is always possible
because in the current set of controllers
two neighboured controllers of different type commutes.
E.g. it is
\code{transpose n . changeTempo r == changeTempo r . transpose n}
and thus the following simplification
\code{transpose 1 . changeTempo 2 . transpose 3 == transpose 4 . changeTempo 2}
is possible.

\begin{haskelllisting}

> mergeControl, mergeControlCompact ::
>       (Music.Control -> Maybe a)
>    -> (a -> Music.T note -> Music.T note)
>    -> (a -> a -> a)
>    -> Music.T note
>    -> Music.T note
> mergeControlCompact extract control merge =
>    let collectControl =
>           Music.switchList
>              (\d n -> ([], Music.atom d n))
>              (\c m -> let cm = collectControl m
>                       in  (c : fst cm, snd cm))
>              ((,) [] . Music.line  . map recurse)
>              ((,) [] . Music.chord . map recurse)
>        recurse m =
>           let cm = collectControl m
>               (xs, cs') = partitionMaybe extract (fst cm)
>               x  = foldl1 merge xs
>               collectedCtrl = if null xs then id else control x
>           in  collectedCtrl (foldr id (snd cm) (map Music.control cs'))
>    in  recurse

> -- more intuitive implementation
> mergeControl extract control merge =
> --   flattenControllers .
> --   CtrlMediumList.mapControl
>    CtrlMedium.foldList
>       CtrlMediumList.prim
>       CtrlMediumList.serial
>       CtrlMediumList.parallel
>       (\cs cm ->
>           let (xs, cs') = partitionMaybe extract cs
>               collectedCtrl =
>                  if null xs then id else control (foldl1 merge xs)
>           in  collectedCtrl (foldr id cm (map Music.control cs'))) .
>    cumulateControllers

> cumulateControllers ::
>       CtrlMediumList.T control a
>    -> CtrlMediumList.T [control] a
> cumulateControllers =
>    CtrlMedium.foldList
>       CtrlMediumList.prim
>       CtrlMediumList.serial
>       CtrlMediumList.parallel
>       (\c m ->
>          let cm = CtrlMedium.control [c] m
>          in  CtrlMedium.switchList
>                 (const cm)
>                 (const cm)
>                 (const cm)
>                 (\cs m' -> CtrlMedium.control (c:cs) m')
>                 m)

> flattenControllers ::
>       CtrlMediumList.T [control] a
>    -> CtrlMediumList.T control a
> flattenControllers =
>    CtrlMedium.foldList
>       CtrlMediumList.prim
>       CtrlMediumList.serial
>       CtrlMediumList.parallel
>       (flip (foldr id) . map CtrlMedium.control)

\end{haskelllisting}

The function \function{removeNeutral} removes controllers
that have no effect.
\begin{haskelllisting}

> removeNeutral :: (Music.Control -> Bool) -> Music.T note -> Music.T note
> removeNeutral isNeutral =
>    let fc c m = if isNeutral c
>                 then m
>                 else Music.control c m
>    in  Music.foldList Music.atom fc Music.line Music.chord

\end{haskelllisting}


Remove redundant \code{Tempo}s.
\begin{haskelllisting}

> tempo =
>    let maybeTempo (Music.Tempo t) = Just t
>        maybeTempo _               = Nothing
>    in  removeNeutral (== Music.Tempo 1) .
>           mergeControl maybeTempo Music.changeTempo (*)

\end{haskelllisting}

Remove redundant \code{Transpose}s.
\begin{haskelllisting}

> transpose =
>    let maybeTranspose (Music.Transpose t) = Just t
>        maybeTranspose _                   = Nothing
>    in  removeNeutral (== Music.Transpose 0) .
>           mergeControl maybeTranspose Music.transpose (+)

\end{haskelllisting}

Change repeated Volume Note Attributes to Phrase Attributes.
\begin{haskelllisting}

> volume =
>    let maybeLoudness (Music.Phrase (Music.Dyn (Music.Loudness t))) = Just t
>        maybeLoudness _ = Nothing
>    in  removeNeutral (== Music.Phrase (Music.Dyn (Music.Loudness 1))) .
>           mergeControl maybeLoudness Music.loudness1 (*)

\end{haskelllisting}

Eliminate \code{Serial} and \code{Parallel} composition
if they contain only one member.
This can be done very general for \type{CtrlMedium.T}.
We have also a version which works on \type{Music.T}.
Since the medium data type supports controllers
there is no longer a real difference between these two functions.
\begin{haskelllisting}

> singletonMedium ::
>    CtrlMediumList.T control a -> CtrlMediumList.T control a
> singletonMedium =
>    CtrlMedium.foldList CtrlMediumList.prim
>       (\ms -> case ms of {[x] -> x; _ -> serial   ms})
>       (\ms -> case ms of {[x] -> x; _ -> parallel ms})
>       (CtrlMedium.control)

> singleton :: Music.T note -> Music.T note
> singleton =
>    Music.foldList Music.atom Music.control
>       (\ms -> case ms of {[x] -> x; _ -> Music.line  ms})
>       (\ms -> case ms of {[x] -> x; _ -> Music.chord ms})

\end{haskelllisting}
