Package jazzparser :: Package utils :: Module chords
[hide private]
[frames] | no frames]

Source Code for Module jazzparser.utils.chords

  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  # Get the logger from the logging system 
 36  logger = logging.getLogger("main_logger") 
 37   
 38   
 39  # Conversions between Lilypond notes and their numeric representation 
 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   
99 -def chord_numeral_to_int(chord_numeral, strict=False):
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 # Map roman numerals to numbers 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 # Use a regular expression to split the chord root into a 131 # its accidental and numeral. 132 root_pattern = re.compile(r'^([b|\#]?)(I{1,3}|I?V|VI{0,2}|X|Y|Z)$') 133 134 # Map accidentals to a numeric adjustment 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 # Map the root name to a number 145 if numeral not in numerals: 146 raise ChordError, "Chord numeral \"%s\" was not recognised." % numeral 147 chord_num = numerals[numeral] 148 # Adjust this number according to the accidental 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
156 -def pitch_class_to_int(chord_numeral):
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 # Adjust this number according to the accidentals 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
182 -def int_to_chord_numeral(chord_int):
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 # Take number mod 12, in case it's not in correct range 199 return ROMAN_NUMERALS[chord_int % 12]
200
201 -def int_to_pitch_class(chord_int):
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 # Take number mod 12, in case it's not in correct range 211 return int_to_note_name[chord_int % 12]
212
213 -def generalise_chord_name(chord_name):
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 # Try building a chord from the chord name 224 chord = Chord.from_name(chord_name) 225 # Only interested in the tetrad type 226 return "X%s" % chord.tetrad_type
227
228 -def interval_observation_from_chord_string_pair(chord1, chord2, type_mapping=None):
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 # Apply a mapping to the chord type if one was given 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
248 -class ChordError(Exception):
249 """ 250 Raised when there's a problem recognising or processing a chord. 251 """ 252 pass
253