\subsection{Conversion functions with default settings} \seclabel{default-performance} \subsubsection{Examples of Player Construction} A ``default player'' called \function{Default.player} (not to be confused with ``deaf player''!) is defined for use when none other is specified in the score; it also functions as a base from which other players can be derived. \function{Default.player} responds only to the \constructor{Velocity} note attribute and to the \constructor{Accent}, \constructor{Staccato}, and \constructor{Legato} phrase attributes. It is defined in \figref{default-Player}. Before reading this code, recall how players are invoked by the \function{Performance.fromMusic} function defined in the last section; in particular, note the calls to \function{playNote} and \function{interpretPhase} defined above. Then note: \begin{enumerate} \item \function{defltPlayNote} is the only function (even in the definition of \function{Performance.fromMusic}) that actually generates an event. It also modifies that event based on an interpretation of each note attribute by the function \function{defltNasHandler}. \item \function{defltNasHandler} only recognizes the \constructor{Velocity} attribute, which it uses to set the event velocity accordingly. \item \function{defltInterpPhrase} calls (mutually recursively) \function{Performance.fromMusic} to interpret a phrase, and then modifies the result based on an interpretation of each phrase attribute by the function \function{defltInterpPhrase}. \item \function{defltInterpPhrase} only recognizes the \constructor{Accent}, \constructor{Staccato}, and \constructor{Legato} phrase attributes. For each of these it uses the numeric argument as a ``scaling'' factor of the volume (for \constructor{Accent}) and duration (for \constructor{Staccato} and \constructor{Legato}). Thus \expression{(Phrase (Legato 1.1) m)} effectively increases the duration of each note in \expression{m} by 10\% (without changing the tempo). \end{enumerate} It should be clear that much of the code in Figure \ref{default-Player} can be re-used in defining a new player. For example, to define a player \function{weird} that interprets note attributes just like \function{Default.player} but behaves differently with respect to phrase attributes, we could write: \begin{haskelllisting} weird :: T weird = Performance.PlayerCons { pname = "Weirdo", playNote = defltPlayNote defltNasHandler, interpretPhrase = liftM . myPhraseInterpreter notatePlayer = defltNotatePlayer () } \end{haskelllisting} and then supply a suitable definition of \function{myPhraseInterpreter}. That definition could also re-use code, in the following sense: suppose we wish to add an interpretation for \constructor{Crescendo}, but otherwise have \function{myPhraseInterpreter} behave just like \function{defltInterpPhrase}. \begin{haskelllisting} myPhraseInterpreter :: PhraseAttribute -> Performance.T time dyn note -> Performance.T time dyn note myPhraseInterpreter (Dyn (Crescendo x)) pf = ... myPhraseInterpreter pa pf = defltInterpPhrase pa pf \end{haskelllisting} \begin{exercise} Fill in the \expression{...} in the definition of \function{myPhraseInterpreter} according to the following strategy: Assume $0<\expression{x}<1$. Gradually scale the volume of each event by a factor of $1.0$ through $1.0+\expression{x}$, using linear interpolation. \end{exercise} \begin{exercise} Choose some of the other phrase attributes and provide interpretations of them, such as \constructor{Diminuendo}, \constructor{Slurred}, \constructor{Trill}, etc. (The \function{trill} functions from \secref{basic-examples} may be useful here.) \end{exercise} {\small \begin{haskelllisting}
> module Haskore.Performance.Default where
> import qualified Haskore.Music       as Music
> import qualified Haskore.Performance as Performance
> import qualified Haskore.Performance.Context as Context
> import qualified Haskore.Performance.Player  as Player
> import qualified Data.EventList.Relative.TimeBody    as TimeList
> import qualified Haskore.Basic.Tempo    as Tempo
> import qualified Haskore.Basic.Duration as Dur
> import qualified Numeric.NonNegative.Class   as NonNeg
> import qualified Numeric.NonNegative.Wrapper as NonNegW
> import Prelude hiding (map)
\end{haskelllisting} } \begin{figure} {\small \begin{haskelllisting}
> -- default is a reserved keyword
> player ::
>    (NonNeg.C time, Fractional time, Real time, Fractional dyn) =>
>    Player.T time dyn note
> player = map "Default"
>
> -- a default PMap that makes everything into a Default.player
> map ::
>    (NonNeg.C time, Fractional time, Real time, Fractional dyn) =>
>    Player.Name -> Player.T time dyn note
> map pname =
>    Performance.PlayerCons {
>       Performance.name            = pname,
>       Performance.playNote        = playNote,
>       Performance.interpretPhrase = interpretPhrase,
>       Performance.notatePlayer    = notatePlayer ()
>    }
>
> playNote :: (Fractional time, Real time) =>
>    Performance.NoteFun time dyn note
> playNote
>    (Performance.Context curDur _ curKey curVelocity) d note =
>        TimeList.singleton 0
>             (Performance.Event {
>                Performance.eventDur       = Dur.toNumber d * curDur,
>                Performance.eventTranspose = curKey,
>                Performance.eventDynamics  = curVelocity,
>                Performance.eventNote      = note } )
>
> interpretPhrase ::
>    (NonNeg.C time, Fractional time, Fractional dyn) =>
>    Performance.PhraseFun time dyn note
> interpretPhrase (Music.Dyn (Music.Accent   x)) = Player.accent x
> interpretPhrase (Music.Art (Music.Staccato x)) = Player.staccatoAbs x
> interpretPhrase (Music.Art (Music.Legato   x)) = Player.legatoAbs x
> interpretPhrase _                              = id
>
> notatePlayer :: () -> Performance.NotateFun
> notatePlayer _ = ()
> context ::
>    (NonNeg.C time, Fractional time, Real time, Fractional dyn) =>
>    Context.T time dyn note
> context =
>    Performance.Context {
>       Performance.contextPlayer     = player,
>       Performance.contextDur        = Tempo.metro 60 Dur.qn,
>       Performance.contextTranspose  = 0,
>       Performance.contextDynamics   = 1
>    }
\end{haskelllisting} } \caption{Definition of default Player \function{Default.player}.} \figlabel{default-Player} \end{figure} {\small \begin{haskelllisting}
> fromMusic ::
>    (Ord note, NonNeg.C time, RealFrac time, Fractional dyn, Ord dyn) =>
>    Music.T note -> Performance.T time dyn note
> fromMusic =
>    Performance.fromMusic map context
>
> fromMusicModifyContext ::
>    (Ord note, NonNeg.C time, RealFrac time, Fractional dyn, Ord dyn) =>
>    (Context.T time dyn note -> Context.T time dyn note) ->
>    Music.T note ->
>    Performance.T time dyn note
> fromMusicModifyContext update =
>    Performance.fromMusic
>       map
>       (update context)
>
> floatFromMusic :: (Ord note) =>
>    Music.T note -> Performance.T NonNegW.Float Float note
> floatFromMusic = fromMusic
>
> paddedFromMusic  ::
>    (Ord note, NonNeg.C time, RealFrac time, Fractional dyn, Ord dyn) =>
>    Music.T note -> Performance.Padded time dyn note
> paddedFromMusic =
>    Performance.paddedFromMusic map context
>
> paddedFromMusicModifyContext ::
>    (Ord note, NonNeg.C time, RealFrac time, Fractional dyn, Ord dyn) =>
>    (Context.T time dyn note -> Context.T time dyn note) ->
>    Music.T note ->
>    Performance.T time dyn note
> paddedFromMusicModifyContext update =
>    Performance.fromMusic
>       map
>       (update context)
\end{haskelllisting} }