1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
<title> Haskore Tutorial: Chords</title>
<body bgcolor="#ffffff"><i>The Haskore Tutorial</i><br><a href="index.html">top</a> <a href="HaskToMidi.html">back</a> <a href="equiv.html">next</a><hr>
<a name="chords"></a><p>
<a name="sect7"></a>
<h2>7<tt> </tt>Representing Chords</h2>
<p>
I have described how to represent chords as values of type
<tt>Music</tt>. However, sometimes it is convenient to treat chords more
abstractly. Rather than think of a chord in terms of its actual
notes, it is useful to think of it in terms of its chord "quality,"
coupled with the key it is played in and the particular voicing used.
For example, we can describe a chord as being a "major triad in root
position, with root middle C." Several approaches have been put
forth for representing this information, and we cannot cover all of
them here. Rather, I will describe two basic representations, leaving
other alternatives to the skill and imagination of the
reader. (For example, Forte prescribes normal forms for chords
in an atonal setting [<a href="tutorial.html#$forte">For73</a>].)<p>
First, one could use a <I>pitch</I> representation, where each note is
represented as its distance from some fixed pitch. <tt>0</tt> is the
obvious fixed pitch to use, and thus, for example, <tt>[0,4,7]
</tt>represents a major triad in root position. The first zero is in some
sense redundant, of course, but it serves to remind us that the chord
is in "normal form." For example, when forming and transforming
chords, we may end up with a representation such as <tt>[2,6,9]</tt>,
which is not normalized; its normal form is in fact <tt>[0,4,7]</tt>.
Thus we define:
<blockquote>A chord is in <I>pitch normal form</I> if the first pitch is zero,
and the subsequent pitches are monotonically increasing.
</blockquote><p>
One could also represent a chord <I>intervalically</I>; i.e. as a
sequence of intervals. A major triad in root position, for example,
would be represented as <tt>[4,3,-7]</tt>, where the last interval
"returns" us to the "origin." Like the <tt>0</tt> in the pitch
representation, the last interval is redundant, but allows us to
define another sense of normal form:
<blockquote>A chord is in <I>interval normal form</I> if the intervals are all
greater than zero, except for the last which must be equal to the
negation of the sum of the others.
</blockquote>
In either case, we can define a chord type as:
<tt> <br>
<br>
> type Chord = [AbsPitch]<br>
<br>
</tt> <p>
We might ask whether there is some advantage, computationally, of
using one of these representations over the other. However, there is
an invertible linear transformation between them, as defined by the
following functions, and thus there is in fact little advantage of one
over the other:
<tt><br>
<br>
> pitToInt :: Chord -> Chord<br>
> pitToInt ch = aux ch<br>
> where aux (n1:n2:ns) = (n2-n1) : aux (n2:ns)<br>
> aux [n] = [head ch - n]<br>
><br>
> intToPit :: Chord -> Chord<br>
> intToPit ch = 0 : aux 0 ch<br>
> where aux p [n] = []<br>
> aux p (n:ns) = n' : aux n' ns where n' = p+n<br>
<br>
</tt> <p>
<p>
<B>Exercise<br>
</B>Show that <tt>pitToInt</tt> and <tt>intToPit</tt> are <I>inverses</I> in the
following sense: for any chord <tt>ch1</tt> in pitch normal form, and
<tt>ch2</tt> in interval normal form, each of length at least two:
<div align=center><tt>intToPit (pitToInt ch1) = ch1<br>
pitToInt (intToPit ch2) = ch2
</tt></div>
<p>
<p>
Another operation we may wish to perform is a test for <I>equality
</I>on chords, which can be done at many levels: based only on chord
quality, taking inversion into account, absolute equality, etc. Since
the above normal forms guarantee a unique representation, equality of
chords with respect to chord quality and inversion is simple: it is
just the standard (overloaded) equality operator on lists. On the
other hand, to measure equality based on chord quality alone, we need
to account for the notion of an <I>inversion</I>.<p>
Using the pitch representation, the inversion of a chord can be
defined as follows:
<tt> <br>
<br>
> pitInvert (p1:p2:ps) = 0 : map (subtract p2) ps ++ [12-p2]<br>
<br>
</tt>
Although we could also directly define a function to invert
a chord given in interval representation, we will simply
define it in terms of functions already defined:
<tt> <br>
<br>
> intInvert = pitToInt . pitInvert . intToPit<br>
<br>
<p>
</tt>We can now determine whether a chord in normal form has the same
quality (but possibly different inversion) as another chord in normal
form, as follows: simply test whether one chord is equal either to the
other chord or to one of its inversions. Since there is only a finite
number of inversions, this is well defined. In Haskell:
<tt> <br>
<br>
> samePitChord ch1 ch2 = <br>
> let invs = take (length ch1) (iterate pitInvert ch1)<br>
> in or (map (==ch2) invs)<br>
><br>
> sameIntChord ch1 ch2 = <br>
> let invs = take (length ch1) (iterate intInvert ch1)<br>
> in or (map (==ch2) invs)<br>
<br>
</tt>
For example, <tt>samePitChord [0,4,7] [0,5,9]</tt> returns <tt>True
</tt>(since <tt>[0,5,9]</tt> is the pitch normal form for the second inversion
of <tt>[0,4,7]</tt>).<p>
<hr><body bgcolor="#ffffff"><i>The Haskore Tutorial</i><br><a href="index.html">top</a> <a href="HaskToMidi.html">back</a> <a href="equiv.html">next</a>
|