%-*- mode: Latex; abbrev-mode: true; auto-fill-function: do-auto-fill -*- %include lhs2TeX.fmt %include myFormat.fmt \out{ \begin{code} -- This code was automatically generated by lhs2tex --code, from the file -- HSoM/Performance.lhs. (See HSoM/MakeCode.bat.) \end{code} } \chapter{Interpretation and Performance} \label{ch:performance} \begin{code} {-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} module Euterpea.Music.Note.Performance where import Euterpea.Music.Note.Music import Euterpea.Music.Note.MoreMusic \end{code} \syn{The first line above is a GHC \emph{pragma} that, in this case, relaxes certain constraints on instance declarations. Specifically, instances cannot normally be declared for type synonyms---but the above pragma overrides that constraint.} So far, our presentation of musical values in Haskell has been mostly structural, i.e.\ \emph{syntactic}. Although we have given an interpretation of the duration of |Music| values (as manifested in |dur|, |takeM|, |dropM|, and so on), we have not given any deeper musical interpretation. What do these musical values actually \emph{mean}, i.e.\ what is their \emph{semantics}, or \emph{interpretation}? The formal process of giving a semantic interpretation to syntactic constructs is very common in computer science, especially in programming language theory. But it is obviously also common in music: the interpretation of music is the very essence of musical performance. However, in conventional music this process is usually informal, appealing to aesthetic judgments and values. What we would like to do is make the process formal in Euterpea---but still flexible, so that more than one interpretation is possible, just as in the human performance of music. \section{Abstract Performance} \label{sec:performance} To begin, we need to say exactly what an abstract \emph{performance} is. Our approach is to consider a performance to be a time-ordered sequence of musical \emph{events}, where each event captures the playing of one individual note. In Haskell: \begin{code} type Performance = [Event] data Event = Event { eTime :: PTime, eInst :: InstrumentName, ePitch :: AbsPitch, eDur :: DurT, eVol :: Volume, eParams :: [Double]} deriving (Show,Eq,Ord) \end{code} \begin{spec} type PTime = Rational type DurT = Rational type Volume = Integer \end{spec} \out{ \begin{code} type PTime = Rational type DurT = Rational \end{code}} \index{field labels} \syn{The data declaration for |Event| uses Haskell's \emph{field label} syntax, also called \emph{record} syntax, and is equivalent to: \begin{spec} data Event = Event PTime InstrumentName AbsPitch DurT Volume [Double] deriving (Show,Eq,Ord) \end{spec} except that the former also defines ``field labels'' |eTime|, |eInst|, |ePitch|, |eDur|, |eVol|, and |eParams|, which can be used to create, update, and select from |Event| values.} \syn{For example, this equation: \begin{spec} e = Event 0 Cello 27 (1/4) 50 [] \end{spec} is equivalent to: \begin{spec} e = Event { eTime = 0, ePitch = 27, eDur = 1/4, eInst = Cello, eVol = 50, eParams = [] } \end{spec} Although more verbose, the latter is also more descriptive, and the order of the fields does not matter (indeed the order here is not the same as above). Field labels can be used to \emph{select} fields from an |Event| value; for example, using the value of |e| above, |eInst e => Cello|, |eDur e => 1/4|, and so on. They can also be used to selectively \emph{update} fields of an existing |Event| value. For example: \begin{spec} e { eInst = Flute } ==> Event 0 Flute 27 (1/4) 50 [] \end{spec} Finally, they can be used selectively in pattern matching: \begin{spec} f (Event { eDur = d, ePitch = p }) = ... d ... p ... \end{spec} Field labels do not change the basic nature of a data type; they are simply a convenient syntax for referring to the components of a data type by name rather than by position.} An event |Event {eTime = s, eInst = i, ePitch = p, eDur = d, eVol = v}| captures the fact that at start time |s|, instrument |i| sounds pitch |p| with volume |v| for a duration |d| (where now duration is measured in seconds, rather than beats). (The |eParams| of an event is for instruments other than MIDI, in particular instruments that we might design on our using the techniques described in Chapter~\ref{ch:sigfuns}. An abstract performance is the lowest of our music representations not yet committed to MIDI or some other low-level computer music representation. In Chapter~\ref{ch:midi} we will discuss how to map a performance into MIDI. \subsection{Context} \label{sec:context} To generate a complete performance of, i.e.\ give an interpretation to, a musical value, we must know the time to begin the performance, and the proper instrument, volume, starting pitch offset, and tempo. We can think of this as the ``context'' in which a musical value is interpreted. This context can be captured formally in Haskell as a data type: \pagebreak \begin{code} data Context a = Context { cTime :: PTime, cPlayer :: Player a, cInst :: InstrumentName, cDur :: DurT, cPch :: AbsPitch, cVol :: Volume, cKey :: (PitchClass, Mode) } deriving Show \end{code} When a |Music| value is interpreted, it will be given an inital context, but as the |Music| value is recursively interpreted, the context will be updated to reflect things like tempo change, transposition, and so on. This will be made clear shortly. The |DurT| component of the context is the duration, in seconds, of one whole note. To make it easier to compute, we can define a ``metronome'' function that, given a standard metronome marking (in beats per minute) and the note type associated with one beat (quarter note, eighth note, etc.) generates the duration of one whole note: \begin{code} metro :: Int -> Dur -> DurT metro setting dur = 60 / (fromIntegral setting * dur) \end{code} Thus, for example, |metro 96 qn| creates a tempo of 96 quarter notes per minute. \syn{|fromIntegral :: (Integral a, Num b) => a -> b| coerces a value whose type is a member of the |Integral| class to a value whose type is a member of the |Num| class. As used here, it is effectively converting the |Int| value |setting| to a |Rational| value, because |dur| is a |Rational| value, |Rational| is a member of the |Num| class, and multiplication has type |(*) :: Num a => a->a->a|.} \subsection{Player Map} \label{sec:player-map} In addition to the context, we also need to know what {\em player} to use; that is, we need a mapping from each |PlayerName| (a string) in a |Music| value to the actual player to be used.\footnote{We do not need a mapping from |InstrumentName|s to instruments, since that is handled in the translation from a performance into MIDI, which is discussed in Chapter \ref{ch:midi}.} The details of what a player is, and how it gives great flexibility to Euterpea, will be explained later in this chapter (Section~\ref{sec:players}). For now, we simply define a type synonym to capture the mapping of |PlayerName| to |Player|: \begin{code} type PMap a = PlayerName -> Player a \end{code} \subsection{Interpretation} \label{sec:perform} Finally, we are ready to give an interpretation to a piece of music, which we do by defining a function |perform|, whose type is: \begin{spec} perform :: PMap a -> Context a -> Music a -> Performance \end{spec} So |perform pm c m| is the |Performance| that results from interpreting |m| using player map |pm| in the initial context |c|. Conceptually, |perform| is perhaps the most important function defined in this textbook, and is shown in Figure \ref{fig:perform}. To help in understanding the definition of |perform|, let's step through the equations one at a time. \begin{figure} \cbox{\small \begin{spec} perform :: PMap a -> Context a -> Music a -> Performance perform pm c@Context {cTime = t, cPlayer = pl, cDur = dt, cPch = k} m = case m of Prim (Note d p) -> playNote pl c d p Prim (Rest d) -> [] m1 :+: m2 -> let c' = c {cTime = t + dur m1 * dt} in perform pm c m1 ++ perform pm c' m2 m1 :=: m2 -> merge (perform pm c m1) (perform pm c m2) Modify (Tempo r) m -> perform pm (c {cDur = dt / r}) m Modify (Transpose p) m -> perform pm (c {cPch = k + p}) m Modify (Instrument i) m -> perform pm (c {cInst = i}) m Modify (KeySig pc mo) m -> perform pm (c {cKey = (pc,mo)}) m Modify (Player pn) m -> perform pm (c {cPlayer = pm pn}) m Modify (Phrase pa) m -> interpPhrase pl pm c pa m \end{spec}} \caption{An abstract |perform| function} \label{fig:perform} \end{figure} \begin{enumerate} \item The interpretation of a note is player dependent. This is handled in |perform| using the |playNote| function, which takes the player as an argument. Precisely how the |playNote| function works is described in Section~\ref{sec:players}, but for now you can think of it as returning a |Performance| (a list of events) with just one event: the note being played. \item In the interpretation of |(:+:)|, note that the |Performance|s of the two arguments are appended together, with the start time of the second |Performance| delayed by the duration of the first (as captured in the context |c'|). The function |dur| (defined in Section \ref{sec:duration}) is used to compute this duration. Note that the interpretation of |(:+:)| is well-defined even for infinite |Music| values. \item In the interpretation of |(:=:)|, the |Performance|s derived from the two arguments are merged into a time-ordered stream. The definition of |merge| is given below: \begin{spec} merge :: Performance -> Performance -> Performance merge [] es2 = es2 merge es1 [] = es1 merge a@(e1:es1) b@(e2:es2) = if e1 < e2 then e1 : merge es1 b else e2 : merge a es2 \end{spec} Note that |merge| is esssentially the same as the |mergeLD| function defined in Section~\ref{sec:lazy-rescue}. \item In the interpretation of |Modify|, first recall the definition of |Control| from Chapter~\ref{sec:music}: \begin{spec} data Control = Tempo Rational -- scale the tempo | Transpose AbsPitch -- transposition | Instrument InstrumentName -- instrument label | Phrase [PhraseAttribute] -- phrase attributes | Player PlayerName -- player label | KeySig PitchClass Mode -- key signature and mode deriving (Show, Eq, Ord) type PlayerName = String data Mode = Major | Minor deriving (Show, Eq, Ord) \end{spec} Each of these six constructors is handled by a separate equation in the definition of |perform|. Note how the context is updated in each case---the |Context|, in general, is the running ``state'' of the performance, and gets updated in several different ways. Also of note is the treatment of |Phrase|. Like the playing of a note, the playing of a phrase is player dependent. This is captured through the function |interpPhrase|, which takes the player as an argument. Like |playNote|, this too, along with the |PhraseAttribute| data type, will be described in full detail in Section~\ref{sec:players}. \end{enumerate} Figure~\ref{fig:PerformBD} is a block diagram showing how |perform| fits into the ``big picture'' of Euterpea. |Music| values are most abstract, |Performance| values are less abstract, and MIDI or audio streams are the least abstract. This chapter focuses on converting a |Music| value into a |Performance|; subsequent chapters will focus on translating a |Performance| into either MIDI (still at the ``note'' level, and fairly straightforward) or audio (at the ``signal'' level, and more complex). \begin{figure}[hbtp] \centering \includegraphics[height=4in]{pics/PerformBD.eps} \caption{Block Diagram of Performance Concepts} \label{fig:PerformBD} \end{figure} %% For example, the interpretation of the |Tempo| constructor involves %% scaling |dt| appropriately and updating the |DurT| field of the %% context. \subsection{Efficiency Concerns} The use of |dur| in the treatment of |(:+:)| can, in the worst case, result in a quadratic time complexity for |perform|. (Why?) A more efficient solution is to have |perform| compute the duration directly, returning it as part of its result. This version of |perform| is shown in Figure \ref{fig:real-perform}. Aside from efficiency, there is a more abstract reason for including duration in the result of |perform|. Namely, the performance of a rest is not just nothing---it is a period of ``silence'' equal in duration to that of the rest. Indeed, John Cage's famous composition \emph{4' 33"}, in which the performer is instructed to play nothing, would otherwise be meaningless.\footnote{In reality this piece is meant to capture extemporaneously the sound of the environment during that period of ``silence.'' \cite{Cage433}} Also note that |merge| compares entire events rather than just start times. This is to ensure that it is commutative, a desirable condition for some of the proofs used later in the text. Here is a more efficient version of |merge| that will work just as well in practice: \begin{code} merge :: Performance -> Performance -> Performance merge [] es2 = es2 merge es1 [] = es1 merge a@(e1:es1) b@(e2:es2) = if eTime e1 < eTime e2 then e1 : merge es1 b else e2 : merge a es2 \end{code} \begin{figure} \cbox{\small \begin{code} perform :: PMap a -> Context a -> Music a -> Performance perform pm c m = fst (perf pm c m) perf :: PMap a -> Context a -> Music a -> (Performance, DurT) perf pm c@Context {cTime = t, cPlayer = pl, cDur = dt, cPch = k} m = case m of Prim (Note d p) -> (playNote pl c d p, d*dt) Prim (Rest d) -> ([], d*dt) m1 :+: m2 -> let (pf1,d1) = perf pm c m1 (pf2,d2) = perf pm (c {cTime = t+d1}) m2 in (pf1++pf2, d1+d2) m1 :=: m2 -> let (pf1,d1) = perf pm c m1 (pf2,d2) = perf pm c m2 in (merge pf1 pf2, max d1 d2) Modify (Tempo r) m -> perf pm (c {cDur = dt / r}) m Modify (Transpose p) m -> perf pm (c {cPch = k + p}) m Modify (Instrument i) m -> perf pm (c {cInst = i}) m Modify (KeySig pc mo) m -> perf pm (c {cKey = (pc,mo)}) m Modify (Player pn) m -> perf pm (c {cPlayer = pm pn}) m Modify (Phrase pas) m -> interpPhrase pl pm c pas m \end{code}} \caption{A more efficient |perform| function} \label{fig:real-perform} \end{figure} %% m1 :=/ m2 -> %% let (pf1,d1) = perf pm c m1 %% (pf2,d2) = perf pm c m2 %% in (merge pf1 pf2, max d1 d2) \section{Players} \label{sec:players} %% \begin{spec} %% module Players (module Players, module Music, module Performance) %% where %% import Music %% import Performance %% \end{spec} Recall from Section~\ref{sec:music} that the |Phrase| constructor in the |Control| data type takes a list of |PhraseAttribute|s as an argument: \begin{spec} data Control = ... | Phrase [PhraseAttribute] -- phrase attributes ... \end{spec} It is now time to unveil the definition of |PhraseAttribute|! Shown fully in Figure \ref{fig:phrase-attributes}, these attributes give us great flexibility in the interpretation process, because they can be interpreted by different players in different ways. For example, how should ``legato'' be interpreted in a performance? Or ``diminuendo?'' Different human players interpret things in different ways, of course, but even more fundamental is the fact that a pianist, for example, realizes legato in a way fundamentally different from the way a violinist does, because of differences in their instruments. Similarly, diminuendo on a piano and diminuendo on a harpsichord are very different concepts. \begin{figure} \cbox{\small \begin{spec} data PhraseAttribute = Dyn Dynamic | Tmp Tempo | Art Articulation | Orn Ornament deriving (Show, Eq, Ord) data Dynamic = Accent Rational | Crescendo Rational | Diminuendo Rational | StdLoudness StdLoudness | Loudness Rational deriving (Show, Eq, Ord) data StdLoudness = PPP | PP | P | MP | SF | MF | NF | FF | FFF deriving (Show, Eq, Ord, Enum) data Tempo = Ritardando Rational | Accelerando Rational deriving (Show, Eq, Ord) data Articulation = Staccato Rational | Legato Rational | Slurred Rational | Tenuto | Marcato | Pedal | Fermata | FermataDown | Breath | DownBow | UpBow | Harmonic | Pizzicato | LeftPizz | BartokPizz | Swell | Wedge | Thumb | Stopped deriving (Show, Eq, Ord) data Ornament = Trill | Mordent | InvMordent | DoubleMordent | Turn | TrilledTurn | ShortTrill | Arpeggio | ArpeggioUp | ArpeggioDown | Instruction String | Head NoteHead | DiatonicTrans Int deriving (Show, Eq, Ord) data NoteHead = DiamondHead | SquareHead | XHead | TriangleHead | TremoloHead | SlashHead | ArtHarmonic | NoHead deriving (Show, Eq, Ord) \end{spec}} \caption{Phrase Attributes} \label{fig:phrase-attributes} \end{figure} In addition to phrase attributes, Euterpea has a notion of \emph{note attributes} that can similarly be interpreted in different ways by different players. This is done by exploiting polymorphism to define a version of |Music| that in addition to pitch, carries a list of note attributes for each individual note: \begin{spec} data NoteAttribute = Volume Int -- MIDI convention: 0=min, 127=max | Fingering Integer | Dynamics String | Params [Double] deriving (Show, Eq) \end{spec} Our goal then is to define a player for music values of type: \begin{code} type Note1 = (Pitch, [NoteAttribute]) type Music1 = Music Note1 \end{code} To facilitate the use of |Music1| values, Euterpea defines the following simple coercion functions: \begin{code} toMusic1 :: Music Pitch -> Music1 toMusic1 = mMap (\p -> (p, [])) toMusic1' :: Music (Pitch, Volume) -> Music1 toMusic1' = mMap (\(p, v) -> (p, [Volume v])) \end{code} Finally, with a slight stretch of the imagination, we can even consider the generation of a \emph{score} as a kind of player: exactly how the music is notated on the written page may be a personal, stylized process. For example, how many, and which staves should be used to notate a particular instrument? %% As another example, the |Key| phrase attribute is needed to implement %% a proper trill or turn, and also how to notate accidentals in a score. To handle these three different kinds of interpretation, Euterpea has a notion of a \emph{player} that ``knows'' about differences with respect to performance and notation. An Euterpean |Player| is a four-tuple consisting of a name and three functions: one for interpreting notes, one for phrases, and one for producing a properly notated score: \pagebreak \begin{code} data Player a = MkPlayer { pName :: PlayerName, playNote :: NoteFun a, interpPhrase :: PhraseFun a, notatePlayer :: NotateFun a } type NoteFun a = Context a -> Dur -> a -> Performance type PhraseFun a = PMap a -> Context a -> [PhraseAttribute] -> Music a -> (Performance, DurT) type NotateFun a = () instance Show a => Show (Player a) where show p = "Player " ++ pName p \end{code} Note that |NotateFun| is just the unit type; this is because notation is currently not implemented in Euterpea. Also note the instance declaration for a |Player|---since its components are mostly functions, which are not default instances of |Show|, we define a simple way to return the |PlayerName|. \subsection{Example of Player Construction} In this section we define a ``default player'' called |defPlayer| (not to be confused with a ``deaf player''!) for use when none other is specified in a score; it also functions as a basis from which other players can be derived. At the upper-most level, |defPlayer| is defined as a four-tuple: \begin{code} defPlayer :: Player Note1 defPlayer = MkPlayer { pName = "Default", playNote = defPlayNote defNasHandler, interpPhrase = defInterpPhrase defPasHandler, notatePlayer = () } \end{code} The remaining functions are defined in Figure \ref{fig:default-Player}. Before reading this code, first review how players are invoked by the |perform| function defined in the last section; in particular, note the calls to |playNote| and |interpPhrase|. We will define |defPlayer| to respond only to the |Volume| note attribute and to the |Accent|, |Staccato|, and |Legato| phrase attributes. \begin{figure} \cbox{\small \begin{code} defPlayNote :: (Context (Pitch,[a]) -> a -> Event-> Event) -> NoteFun (Pitch, [a]) defPlayNote nasHandler c@(Context cTime cPlayer cInst cDur cPch cVol cKey) d (p,nas) = let initEv = Event { eTime = cTime, eInst = cInst, eDur = d * cDur, eVol = cVol, ePitch = absPitch p + cPch, eParams = [] } in [ foldr (nasHandler c) initEv nas ] defNasHandler :: Context a -> NoteAttribute -> Event -> Event defNasHandler c (Volume v) ev = ev {eVol = v} defNasHandler c (Params pms) ev = ev {eParams = pms} defNasHandler _ _ ev = ev defInterpPhrase :: (PhraseAttribute -> Performance -> Performance) -> ( PMap a -> Context a -> [PhraseAttribute] -> --PhraseFun Music a -> (Performance, DurT) ) defInterpPhrase pasHandler pm context pas m = let (pf,dur) = perf pm context m in (foldr pasHandler pf pas, dur) defPasHandler :: PhraseAttribute -> Performance -> Performance defPasHandler (Dyn (Accent x)) = map (\e -> e {eVol = round (x * fromIntegral (eVol e))}) defPasHandler (Art (Staccato x)) = map (\e -> e {eDur = x * eDur e}) defPasHandler (Art (Legato x)) = map (\e -> e {eDur = x * eDur e}) defPasHandler _ = id \end{code}} \caption{Definition of default player |defPlayer|.} \label{fig:default-Player} \end{figure} % defNotatePlayer :: a -> () % defNotatePlayer _ = () Then note: \begin{enumerate} \item |defPlayNote| is the only function (even in the definition of |perform|) that actually generates an event. It also modifies that event based on an interpretation of each note attribute by the function |defNasHandler|. \item |defNasHandler| only recognizes the |Volume| attribute, which it uses to set the event volume accordingly. \item |defInterpPhrase| calls (mutually recursively) |perform| to interpret a phrase, and then modifies the result based on an interpretation of each phrase attribute by the function |defPasHandler|. \item |defPasHandler| only recognizes the |Accent|, |Staccato|, and |Legato| phrase attributes. For each of these it uses the numeric argument as a ``scaling'' factor of the volume (for |Accent|) and duration (for |Staccato| and |Legato|). Thus |Modify (Phrase [Legato (5/4)]) m| effectively increases the duration of each note in |m| by 25\% (without changing the tempo). \end{enumerate} \subsection{Deriving New Players From Old Ones} \label{sec:new-player} It should be clear that much of the code in Figure \ref{fig:default-Player} can be re-used in defining a new player. For example, to define a player |newPlayer| that interprets note attributes just like |defPlayer| but behaves differently with respect to certain phrase attributes, we could write: \begin{spec} newPlayer :: Player (Pitch, [NoteAttribute]) newPlayer = MkPlayer { pName = "NewPlayer", playNote = defPlayNote defNasHandler, interpPhrase = defInterpPhrase myPasHandler, notatePlayer = () } \end{spec} and then supply a suitable definition of |myPasHandler|. Better yet, we could just do this: \begin{spec} newPlayer :: Player (Pitch, [NoteAttribute]) newPlayer = defPlayer { pName = "NewPlayer", interpPhrase = defInterpPhrase myPasHandler } \end{spec} This version uses the ``record update'' syntax to directly derive the new player from |defPlayer|. The definition of |myPasHandler| can also re-use code, in the following sense: suppose we wish to add an interpretation for |Crescendo|, but otherwise have |myPasHandler| behave just like |defPasHandler|. \begin{spec} myPasHandler :: PhraseAttribute -> Performance -> Performance myPasHandler (Dyn (Crescendo x)) pf = ... myPasHandler pa pf = defPasHandler pa pf \end{spec} \todo{Explain more... in particular, how ``inheritance'' works.} \subsection{A Fancy Player} \label{sec:fancy-player} Figure \ref{fancy-Player} defines a more sophisticated player called |fancyPlayer| that knows all that |defPlayer| knows, and more. Note that |Slurred| is different from |Legato| in that it does not extend the duration of the {\em last} note(s). The behavior of |Ritardando x| can be explained as follows. We would like to ``stretch'' the time of each event by a factor from $0$ to $x$, linearly interpolated based on how far along the musical phrase the event occurs. I.e., given a start time $t_0$ for the first event in the phrase, total phrase duration $D$, and event time $t$, the new event time $t'$ is given by: \[ t' = (1 + \frac{t-t_0}{D}x)(t-t_0) + t_0 \] Further, if $d$ is the duration of the event, then the end of the event $t+d$ gets stretched to a new time $t_d'$ given by: \[ t_d' = (1 + \frac{t+d-t_0}{D}x)(t+d-t_0) + t_0 \] The difference $t_d' - t'$ gives us the new, stretched duration $d'$, which after simplification is: \[ d' = (1 + \frac{2(t-t_0)+d}{D}x)\ d \] |Accelerando| behaves in exactly the same way, except that it shortens event times rather than lengthening them. And a similar but simpler strategy explains the behaviors of |Crescendo| and |Diminuendo|. \pagebreak \section{Putting it all Together} The |play| function in Euterpea ueses a default player map and a default context that are defined as follows: \begin{code} defPMap :: PMap Note1 defPMap "Fancy" = fancyPlayer defPMap "Default" = defPlayer defPMap n = defPlayer { pName = n } defCon :: Context Note1 defCon = Context { cTime = 0, cPlayer = fancyPlayer, cInst = AcousticGrandPiano, cDur = metro 120 qn, cPch = 0, cKey = (C, Major), cVol = 127 } \end{code} Note that if anything other than a |"Fancy"| or |"Default"| player is specified in the |Music| value, such as |player "Strange" m|, then the default player |defPlayer| is used, and given the name |"Strange"|. If instead we wish to use our own player, say |newPlayer| defined in Section \ref{sec:new-player}, then a new player map can be defined, such as: \begin{spec} myPMap :: PlayerName -> Player Note1 myPMap "NewPlayer" = newPlayer myPMap p = defPMap p \end{spec} Similarly, different versions of the context can be defined based on a user's needs. We could, then, use these versions of player maps and contexts to invoke the |perform| function to generate an abstract |Performance|. Of course, we ultimately want to hear our music, not just see an abstract |Performance| displayed on our computer screen. Recall that |play|'s type signature is: \begin{spec} play :: Performable a => Music a -> IO () \end{spec} To allow using different player maps and contexts, Euterpea also has a version of |play| called |playA| whose type signature is: \begin{spec} playA :: Performable a => PMap Note1 -> Context Note1 -> Music a -> IO () \end{spec} For example, to play a |Music| value |m| using |myPMap| defined above and the default context |defCon|, we can do: \begin{spec} playA myPMap defCon m \end{spec} In later chapters we will learn more about |play|, and how it converts a |Performance| into MIDI events that eventually are heard through your computer's sound card. \vspace{.1in}\hrule \begin{exercise}{\em Fill in the |...| in the definition of |myPasHandler| according to the following strategy: Gradually scale the volume of each event in the performance by a factor of |1| through |1+x|, using linear interpolation.} \end{exercise} \begin{exercise}{\em Choose some of the other phrase attributes and provide interpretations for them. (Hint: As in |fancyPlayer|, you may not be able to use the ``|pasHandler|'' approach to implement some of the phrase attributes. For example, for a proper treatment of |Trill| (and similar ornaments) you will need to access the |cKey| field in the context.)} \end{exercise} \begin{exercise}{\em Define a player |myPlayer| that appropriately handles the |Pedal| articulation and both the |ArpeggioUp| and |ArpeggioDown| ornamentations. You should define |myPlayer| as a derivative of |defPlayer| or |newPlayer|.} \end{exercise} \begin{exercise}{\em Define a player |jazzMan| (or |jazzWoman| if you prefer) that plays a melody using a jazz ``swing'' feel. Since there are different kinds and degrees of swing, we can be more specific as follows: whenever there is a sequence of two eighth notes, they should be interpreted instead as a quarter note followed by an eighth note, but with tempo 3/2. So in essence, the first note is lengthened, and the second note is shortened, so that the first note is twice as long as the second, but they still take up the same amount of overall time. (Hint: There are several ways to solve this problem. One surprisingly effective and straightforward solution is to implement |jazzMan| as a |NoteFun|, not a |PhraseFun|. In jazz, if an eighth note falls on a quarter-note beat it is said to fall on the ``downbeat,'' and the eighth notes that are in between are said to fall on the ``upbeat.'' For example, in the phrase |c 4 en :+: d 4 en :+: e 4 en :+: f 4 en|, the C and E fall on the downbeat, and the D and F fall on the upbeat. So to get a ``swing feel,'' the notes on the down beat need to be lengthened, and ones on the upbeat need to be delayed and shortened. Whether an event falls on a downbeat or upbeat can be determined from the |cTime| and |cDur| of the context.)} \end{exercise} \begin{exercise}{\em Implement the ornamentation |DiatonicTrans|, which is intended to be a ``diatonic tranposition'' of a phrase within a particular key. The argument to |DiatonicTrans| is an integer representing the number of \emph{scale degrees} to do the transposition. For example, the diatonic transposition of |c 4 en :+: d 4 en :+: e 4 en| in C major by 2 scale degrees should yield |e 4 en :+: f 4 en :+: g 4 en|, whereas in G major should yield |e 4 en :+: fs 4 en :+: g 4 en|. (Hint: You will need to access the key from the context (using |cKey|). Thus, as with |fancyPlayer|, you may not be able to use the ``|pasHandler|'' approach to solve this problem.)} \end{exercise} %% but the trickier part is how to treat the |cPch| field. Once a %% |Performance| is generated, you can think of each abolute pitch as %% being relative to a zero offset.} \vspace{.1in}\hrule \vspace{.1in} \begin{figure} \todo{This code has errors and needs to be fixed.} \cbox{\small \begin{code} fancyPlayer :: Player (Pitch, [NoteAttribute]) fancyPlayer = MkPlayer { pName = "Fancy", playNote = defPlayNote defNasHandler, interpPhrase = fancyInterpPhrase, notatePlayer = () } fancyInterpPhrase :: PhraseFun a fancyInterpPhrase pm c [] m = perf pm c m fancyInterpPhrase pm c@Context { cTime = t, cPlayer = pl, cInst = i, cDur = dt, cPch = k, cVol = v} (pa:pas) m = let pfd@(pf,dur) = fancyInterpPhrase pm c pas m loud x = fancyInterpPhrase pm c (Dyn (Loudness x) : pas) m stretch x = let t0 = eTime (head pf); r = x/dur upd (e@Event {eTime = t, eDur = d}) = let dt = t-t0 t' = (1+dt*r)*dt + t0 d' = (1+(2*dt+d)*r)*d in e {eTime = t', eDur = d'} in (map upd pf, (1+x)*dur) inflate x = let t0 = eTime (head pf); r = x/dur upd (e@Event {eTime = t, eVol = v}) = e {eVol = round ( (1+(t-t0)*r) * fromIntegral v)} in (map upd pf, dur) in case pa of Dyn (Accent x) -> ( map (\e-> e {eVol = round (x * fromIntegral (eVol e))}) pf, dur) Dyn (StdLoudness l) -> case l of PPP -> loud 40; PP -> loud 50; P -> loud 60 MP -> loud 70; SF -> loud 80; MF -> loud 90 NF -> loud 100; FF -> loud 110; FFF -> loud 120 Dyn (Loudness x) -> fancyInterpPhrase pm c{cVol = round x} pas m Dyn (Crescendo x) -> inflate x ; Dyn (Diminuendo x) -> inflate (-x) Tmp (Ritardando x) -> stretch x ; Tmp (Accelerando x) -> stretch (-x) Art (Staccato x) -> (map (\e-> e {eDur = x * eDur e}) pf, dur) Art (Legato x) -> (map (\e-> e {eDur = x * eDur e}) pf, dur) Art (Slurred x) -> let lastStartTime = foldr (\e t -> max (eTime e) t) 0 pf setDur e = if eTime e < lastStartTime then e {eDur = x * eDur e} else e in (map setDur pf, dur) Art _ -> pfd Orn _ -> pfd \end{code}} \caption{Definition of Player |fancyPlayer|.} \label{fancy-Player} \end{figure} \out{ Generating Performances ----------------------- Make the default translation to Performance as a class in order to deal with both Music Pitch and Music Note1: \begin{code} class Performable a where perfDur :: PMap Note1 -> Context Note1 -> Music a -> (Performance, DurT) \end{code} Using the defaults below, from a Music value we can generate a Performance: \begin{code} instance Performable Note1 where perfDur pm c m = perf pm c m instance Performable Pitch where perfDur pm c = perfDur pm c . toMusic1 instance Performable (Pitch, Volume) where perfDur pm c = perfDur pm c . toMusic1' defToPerf :: Performable a => Music a -> Performance defToPerf = fst . perfDur defPMap defCon toPerf :: Performable a => PMap Note1 -> Context Note1 -> Music a -> Performance toPerf pm con = fst . perfDur pm con \end{code} }