myTestlll-1.0.0: HSoM/Performance.lhs
%-*- 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}
}