\subsubsection{Pitch} \seclabel{pitch} Perhaps the most basic musical idea is that of a \keyword{pitch}, which consists of an \keyword{octave} and a \keyword{pitch class} (i.e. one of 12 semi-tones, cf. \secref{discussion:pitch}): \begin{haskelllisting} > module Haskore.Basic.Pitch where > import Data.Ix(Ix) > type T = (Octave, Class) > data Class = Cf | C | Cs | Df | D | Ds | Ef | E | Es | Ff | F | Fs > | Gf | G | Gs | Af | A | As | Bf | B | Bs > deriving (Eq,Ord,Ix,Enum,Show,Read) > type Octave = Int \end{haskelllisting} So a \type{Pitch.T} is a pair consisting of a pitch class and an octave. Octaves are just integers, but we define a datatype for pitch classes, since distinguishing enharmonics (such as $G^\#$ and $A^b$) may be important (especially for notation). \figref{note-freqs} shows the meaning of the some \type{Pitch.T} values. \begin{figure} \begin{center} \begin{tabular}{llr} $A_2$ & \code{(-3,A)} & 27.5 Hz \\ $A_1$ & \code{(-2,A)} & 55.0 Hz \\ $A $ & \code{(-1,A)} & 110.0 Hz \\ $a $ & \code{( 0,A)} & 220.0 Hz \\ $a^1$ & \code{( 1,A)} & 440.0 Hz \\ $a^2$ & \code{( 2,A)} & 880.0 Hz \end{tabular} \end{center} \caption{Note names, Haskore representations and frequencies.} \figlabel{note-freqs} \end{figure} Treating pitches simply as integers is useful in many settings, so let's also define some functions for converting between \type{Pitch.T} values and \type{Pitch.Absolute} values (integers): \begin{haskelllisting} > type Absolute = Int > type Relative = Int > > toInt :: T -> Absolute > toInt (oct,pc) = 12*oct + classToInt pc > > fromInt :: Absolute -> T > fromInt ap = > let (oct, n) = divMod ap 12 > in (oct, [C,Cs,D,Ds,E,F,Fs,G,Gs,A,As,B] !! n) > > classToInt :: Class -> Relative > classToInt pc = case pc of > Cf -> -1; C -> 0; Cs -> 1 -- or should Cf be 11? > Df -> 1; D -> 2; Ds -> 3 > Ef -> 3; E -> 4; Es -> 5 > Ff -> 4; F -> 5; Fs -> 6 > Gf -> 6; G -> 7; Gs -> 8 > Af -> 8; A -> 9; As -> 10 > Bf -> 10; B -> 11; Bs -> 12 -- or should Bs be 0? \end{haskelllisting} Now two functions for parsing and formatting pitch classes in a more human way, that is using '\#' and 'b' suffixes instead of 's' and 'f'. We do not simply use \begin{haskelllisting} > classParse :: ReadS Class > classParse (p:'#':r) = reads (p:'s':r) > classParse (p:'b':r) = reads (p:'f':r) > classParse r = reads r > classFormat :: Class -> ShowS > classFormat pc = > let (p:r) = show pc > in (p:) . > case r of > [] -> id > 's':[] -> ('#':) > 'f':[] -> ('b':) > _ -> error ("classFormat: Pitch.Class.show must not return suffixes" ++ > " other than 's' and 'f'") \end{haskelllisting} Using \type{Pitch.Absolute} we can compute the frequency associated with a pitch: \begin{haskelllisting} > intToFreq :: Floating a => Absolute -> a > intToFreq ap = 440 * 2 ** (fromIntegral (ap - toInt (1,A)) / 12) \end{haskelllisting} We can also define a function \function{Pitch.transpose}, which transposes pitches (analogous to \function{Music.transpose}, which transposes values of type \type{Music.T}): \begin{haskelllisting} > transpose :: Relative -> T -> T > transpose i p = fromInt (toInt p + i) \end{haskelllisting} \begin{exercise} Show that\ \ \code{toInt\ .\ fromInt = id}, and, up to enharmonic equivalences,\newline \code{fromInt\ .\ toInt = id}. \end{exercise} \begin{exercise} Show that\ \ \code{transpose i (transpose j p) = transpose (i+j) p}. \end{exercise}