1 """Chord processing utilities.
2
3 A library of utility functions used throughout the Jazz Parser relating
4 to chord processing in the input.
5
6 """
7 """
8 ============================== License ========================================
9 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
10
11 This file is part of The Jazz Parser.
12
13 The Jazz Parser is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 The Jazz Parser is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
25
26 ============================ End license ======================================
27
28 """
29 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
30
31 import xml.dom.minidom
32 import re, copy
33 import logging
34
35
36 logger = logging.getLogger("main_logger")
37
38
39
40 ly_note_to_int = {"c" : 0, "C" : 0,\
41 "d" : 2, "D" : 2,\
42 "e" : 4, "E" : 4,\
43 "f" : 5, "F" : 5,\
44 "g" : 7, "G" : 7,\
45 "a" : 9, "A" : 9,\
46 "b" : 11, "B" : 11,\
47 "r" : None }
48
49 ly_note_to_base_int = {"c" : 0, "C" : 0,\
50 "d" : 1, "D" : 1,\
51 "e" : 2, "E" : 2,\
52 "f" : 3, "F" : 3,\
53 "g" : 4, "G" : 4,\
54 "a" : 5, "A" : 5,\
55 "b" : 6, "B" : 6,\
56 "r" : None }
57
58 int_to_ly_note = { 0 : "c",\
59 1 : "cis",\
60 2 : "d",\
61 3 : "dis",\
62 4 : "e",\
63 5 : "f",\
64 6 : "fis",\
65 7 : "g",\
66 8 : "gis",\
67 9 : "a",\
68 10: "ais",\
69 11: "b",\
70 None: "r"}
71
72 int_to_note_name = { 0 : "C", \
73 1 : "Db", \
74 2 : "D", \
75 3 : "Eb", \
76 4 : "E", \
77 5 : "F", \
78 6 : "Gb", \
79 7 : "G", \
80 8 : "Ab", \
81 9 : "A", \
82 10: "Bb", \
83 11: "B" }
84
85 ROMAN_NUMERALS = { 0 : "I",
86 1 : "bII",
87 2 : "II",
88 3 : "bIII",
89 4 : "III",
90 5 : "IV",
91 6 : "#IV",
92 7 : "V",
93 8 : "bVI",
94 9 : "VI",
95 10 : "bVII",
96 11 : "VII" }
97
98
100 """
101 Given a chord numeral (e.g. "I" or "bVII"), returns the integer
102 that corresponds to this chord root.
103 Returns None if input is either a chord variable ("X", "Y") or
104 itself None.
105 If strict is set, doesn't allow variable names.
106
107 """
108 if strict:
109 numerals = { "I" : 0,
110 "II" : 2,
111 "III" : 4,
112 "IV" : 5,
113 "V" : 7,
114 "VI" : 9,
115 "VII" : 11, }
116 root_pattern = re.compile(r'^([b|\#]?)(I{1,3}|I?V|VI{0,2})$')
117 else:
118
119 numerals = { "I" : 0,
120 "II" : 2,
121 "III" : 4,
122 "IV" : 5,
123 "V" : 7,
124 "VI" : 9,
125 "VII" : 11,
126 "X" : None,
127 "Y" : None,
128 "Z" : None,
129 None : None }
130
131
132 root_pattern = re.compile(r'^([b|\#]?)(I{1,3}|I?V|VI{0,2}|X|Y|Z)$')
133
134
135 accidentals = { "#" : 1, "" : 0, "b" : -1 }
136
137 result = root_pattern.search(chord_numeral)
138 if result is None:
139 raise ChordError, "The string '%s' cannot be parsed as a chord" % chord_numeral
140 result = result.groups()
141 accidental = result[0]
142 numeral = result[1]
143
144
145 if numeral not in numerals:
146 raise ChordError, "Chord numeral \"%s\" was not recognised." % numeral
147 chord_num = numerals[numeral]
148
149 if chord_num is not None:
150 if accidental not in accidentals:
151 raise ChordError, "Accidental \"%s\" was not recognised." \
152 % accidental
153 chord_num += accidentals[accidental]
154 return chord_num
155
157 """ Like L{chord_numeral_to_int}, but for pitch class labels. """
158 pcs = { "C" : 0,
159 "D" : 2,
160 "E" : 4,
161 "F" : 5,
162 "G" : 7,
163 "A" : 9,
164 "B" : 11, }
165 root_pattern = re.compile(r'^([A-G])(b*|\#*)$')
166
167 result = root_pattern.search(chord_numeral)
168 if result is None:
169 raise ChordError, "The string '%s' cannot be parsed as a chord" % \
170 chord_numeral
171 pc_str,accidental_str = result.groups()
172
173 pc = pcs[pc_str]
174
175 if accidental_str:
176 if accidental_str[0] == "#":
177 pc += len(accidental_str)
178 elif accidental_str[0] == "b":
179 pc -= len(accidental_str)
180 return pc % 12
181
183 """
184 Given an internal integer representation of a chord root (i.e. a
185 note of the scale), returns the roman numeral as a string. This
186 will always use the same convention for #s and bs, so may not be
187 the same as the numeral that generated the note number.
188
189 The input numbers 0-11 correspond to I-VII in the scale. The input
190 need to be in this range. Outside it, numbers will be mapped into
191 this range by "% 12".
192
193 Returns "X" if input is None.
194
195 """
196 if chord_int is None:
197 return "X"
198
199 return ROMAN_NUMERALS[chord_int % 12]
200
202 """
203 Like L{int_to_chord_numeral}, but outputs a pitch class name instead of
204 roman numeral. Returns "X" if input is None.
205
206 """
207 if chord_int is None:
208 return "X"
209 else:
210
211 return int_to_note_name[chord_int % 12]
212
214 """
215 The grammar generalises over chord names, using X to mean "any
216 roman numeral chord root". When a chord name comes as input to
217 the parser, say "IIm", we look up not "IIm", but "Xm".
218
219 Given any chord name, this function returns the generalised
220 chord name to look up in the grammar.
221 """
222 from jazzparser.data import Chord
223
224 chord = Chord.from_name(chord_name)
225
226 return "X%s" % chord.tetrad_type
227
229 """
230 Given two strings representing chords, produces a string representing
231 a chord observation of the form x-t, where x is the interval between
232 the chords (numeric) and t is the type of the first chord.
233 """
234 from jazzparser.data import Chord
235 chord1 = Chord.from_name(chord1)
236 if chord2 is None:
237 interval = ""
238 else:
239 chord2 = Chord.from_name(chord2)
240 interval = "%d" % Chord.interval(chord1,chord2)
241
242 if type_mapping is not None:
243 ctype = type_mapping[chord1.type]
244 else:
245 ctype = chord1.type
246 return "%s-%s" % (interval, ctype)
247
249 """
250 Raised when there's a problem recognising or processing a chord.
251 """
252 pass
253