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