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

Source Code for Module jazzparser.utils.tonalspace

  1  """Tonal space manipulations and analysis. 
  2   
  3  Note that this module is only intended for formalism-independent  
  4  tonal space manipulations. Functions should not rely on representations  
  5  that are formalism-specific, like  
  6  L{TonalDenotation<jazzparser.formalisms.music_keyspan.semantics.TonalDenotation>}. 
  7  They may use things like coordinates, which could be produced from  
  8  an formalism-specific semantics. 
  9   
 10  """ 
 11  """ 
 12  ============================== License ======================================== 
 13   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
 14    
 15   This file is part of The Jazz Parser. 
 16    
 17   The Jazz Parser is free software: you can redistribute it and/or modify 
 18   it under the terms of the GNU General Public License as published by 
 19   the Free Software Foundation, either version 3 of the License, or 
 20   (at your option) any later version. 
 21    
 22   The Jazz Parser is distributed in the hope that it will be useful, 
 23   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 24   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 25   GNU General Public License for more details. 
 26    
 27   You should have received a copy of the GNU General Public License 
 28   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 29   
 30  ============================ End license ====================================== 
 31   
 32  """ 
 33  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
 34   
 35   
36 -def nearest_neighbour(base_coord, root_number):
37 """ 38 Returns the coordinate of the point with the given root number 39 that is closest to the given point base_coord. Coordinates are 40 represented as (x,y) tuples. The root number is the semitone 41 number of the root (0->I, 1->bII, etc). 0 is assumed to be the 42 ET equivalence set including the central point (0,0). 43 44 @rtype: (x,y) coordinate 45 @return: the location of the point with the given ET root number 46 that is closest to the base point. 47 48 """ 49 base_x, base_y = base_coord 50 # Get the root number of the base coord 51 base_root = (7*base_x + 4*base_y) % 12 52 # Decide which nearby point to pick, relative to the base, by 53 # looking at the interval 54 interval = (root_number - base_root) % 12 55 # Now we just consult the predefined mapping of intervals to nearest 56 # neighbours: 57 # 9 4 11 6 58 # 10 5 0 7 2 59 # 1 8 3 60 rel_coord = { 61 0 : (0,0), 62 1 : (-1,-1), 63 2 : (2,0), 64 3 : (1,-1), 65 4 : (0,1), 66 5 : (-1,0), 67 6 : (-2,-1), 68 7 : (1,0), 69 8 : (0,-1), 70 9 : (-1,1), 71 10 : (-2,0), 72 11 : (1,1) 73 }[interval] 74 # Now add this to the base coord to get the actual coord of the neighbour 75 return (base_x+rel_coord[0], base_y+rel_coord[1])
76
77 -def coordinates_to_roman_names(coords):
78 """ 79 Given a list of tonal space coordinates, return a list of strings 80 giving the roman numeral names of the points. The name is the 81 unambiguous unique specifier of the point (e.g. bbIII++). 82 83 """ 84 return [coordinate_to_roman_name(coord) for coord in coords]
85
86 -def coordinate_to_roman_name(coord, sharp="#", flat="b", plus="+", minus="-", 87 names=None, accidentals_after=False):
88 """ 89 Given a coordinate (x,y), returns the unique roman numeral name of 90 that point in the space, assuming that I is at (0,0). 91 92 """ 93 region = coordinate_key_region(coord) 94 # Work out where this coordinate lies within its local region 95 x,y = coordinate_within_region(coord) 96 # This gives us the base name (without accidentals or +/-s) 97 if names is None: 98 roman_name = { 99 (0,0) : "IV", 100 (1,0) : "I", 101 (2,0) : "V", 102 (3,0) : "II", 103 (0,1) : "VI", 104 (1,1) : "III", 105 (2,1) : "VII" 106 }[(x,y)] 107 else: 108 roman_name = names[(x,y)] 109 # The accidentals are defined by the y value of the region's identifier 110 acc_count = region[1] 111 if acc_count > 0: 112 accidentals = sharp * acc_count 113 elif acc_count < 0: 114 accidentals = flat * (-1*acc_count) 115 else: 116 accidentals = "" 117 # The -/+s are simply a function of the x coordinate 118 plus_count = (coord[0] + 1) / 4 119 if plus_count > 0: 120 mods = plus * plus_count 121 elif plus_count < 0: 122 mods = minus * (-1*plus_count) 123 else: 124 mods = "" 125 if accidentals_after: 126 return u"%s%s%s" % (roman_name, accidentals, mods) 127 else: 128 return u"%s%s%s" % (accidentals, roman_name, mods)
129
130 -def coordinate_to_alpha_name_c(*args, **kwargs):
131 """ 132 Does the same as L{coordinate_to_roman_name}, but generates alphabetic 133 note names in the key of C (i.e. (0,0)=C). 134 135 """ 136 names = { 137 (0,0) : "F", 138 (1,0) : "C", 139 (2,0) : "G", 140 (3,0) : "D", 141 (0,1) : "A", 142 (1,1) : "E", 143 (2,1) : "B" 144 } 145 kwargs['names'] = names 146 kwargs['accidentals_after'] = True 147 return coordinate_to_roman_name(*args, **kwargs)
148
149 -def coordinate_key_region(coord):
150 """ 151 Given a tonal space coordinate, returns the 2D identifier of the 152 key region that it lies in. A key region is the 153 not-quite-rectangular region of notes in a major scale. The central 154 one contains IV (at (-1,0)), rightwards to II ((2,0)), and VI 155 ((-1,1)), rightwards to VII ((1,0)). These regions are tessellated 156 across the infinite space. 157 158 The coordinate returned identifies the region. (0,0) is the central 159 region, with bottom left at point (-1,0). (1,0) is strictly to the 160 right and down one, so has its bottom left at (-1,3). 161 162 """ 163 x,y = coord 164 return ((y + 2*x + 2) / 7, (4*y + x + 1) /7)
165
166 -def coordinate_within_region(coord):
167 """ 168 Given a tonal space coordinate, returns the coordinate of this point 169 relative to the bottom left corner of the local key region in which 170 it lies (see L{coordinate_key_region}). 171 172 This coordinate will be among (0,0),...,(3,0),(0,1),...,(2,1). 173 174 """ 175 x,y = coord 176 spacex, spacey = coordinate_key_region(coord) 177 localx = x - 4*spacex + spacey + 1 178 localy = y - 2*spacey + spacex 179 return localx,localy
180
181 -def equate_ends(coords0, coords1):
182 """ 183 Translates the second list of coordinates so that it ends at the 184 same point as the first and returns the result. 185 186 """ 187 # Work out how much to translate by 188 shift = (coords0[-1][0] - coords1[-1][0]), (coords0[-1][1] - coords1[-1][1]) 189 # Shift all the points of coords1 by this amount 190 return [(x+shift[0], y+shift[1]) for (x,y) in coords1]
191
192 -def coordinate_to_et(coord):
193 """ 194 Takes a point in the tonal space and returns the number of 195 semitones above the origin's pitch that point would be in 196 equal temperament. 197 198 The coordinate is 3-dimensional. 199 200 """ 201 x,y,z = coord 202 # We go 7 semitones up for every right step 203 # And 4 for every up step 204 return (7*x + 4*y + 12*z)
205
206 -def coordinate_to_et_2d(coord):
207 """ 208 2-dimensional version of L{coordinate_to_et}. Returns an interval 209 within one octave upwards ([0-12]). 210 211 """ 212 x,y=coord 213 return coordinate_to_et((x,y,0))%12
214
215 -def cents_to_pitch_ratio(cents):
216 """ 217 Converts a number of cents (tuning theory unit of pitch ratio) into 218 a floating point pitch multiplier. 219 220 @type cents: float 221 @param cents: number of cents 222 223 """ 224 return 2 ** (float(cents)/1200)
225
226 -def pitch_ratio_to_cents(ratio):
227 """ 228 Inverse of L{cents_to_pitch_ratio}. Given a pitch multiplier, 229 returns the equivalent interval expressed in cents. 230 231 """ 232 import math 233 return float(1200) * math.log(float(ratio), 2)
234 235
236 -def tonal_space_pitch(coord):
237 """ 238 Given a 3D coordinate in the tonal space, returns the pitch ratio 239 of that point from the origin's pitch. 240 x: fifths 241 y: thirds 242 z: octaves 243 244 """ 245 import math 246 x,y,z = coord 247 return math.pow(2, z) * math.pow(1.5, x) * math.pow(1.25, y)
248
249 -def tonal_space_pitch_2d(coord):
250 """ 251 Given a 2D coordinate in the tonal space, returns the pitch ratio 252 of that point from the origin's pitch assuming that the point is 253 within one octave above the origin. 254 255 """ 256 x,y = coord 257 pitch = tonal_space_pitch((x,y,0)) 258 # Get it within an octave 259 if pitch < 1.0: 260 ratio = 2.0 261 else: 262 ratio = 0.5 263 while pitch < 1.0 or pitch >= 2.0: 264 pitch *= ratio 265 return pitch
266
267 -def tonal_space_et_pitch(coord):
268 """ 269 Given a (2D) point in the tonal space, returns the pitch ratio from the 270 origin that it would have in the equal-temperament wrapped space. 271 272 """ 273 if len(coord) == 3: 274 return et_interval(coordinate_to_et(coord)) 275 else: 276 return et_interval(coordinate_to_et_2d(coord))
277
278 -def et_interval(st=1, oct=0):
279 """ 280 Frequency ratio corresponding to a number of ET semitones and octaves. 281 282 """ 283 import math 284 semitones = oct*12 + st 285 return math.exp(math.log(2.0)/12*semitones)
286 et_semitone = et_interval(1) 287 288
289 -def add_z_coordinates(coords, center=(0,0,0), pitch_range=1):
290 """ 291 Given a list of (x,y) coordinates, adds a z-coordinate to each 292 such that the pitch of the notes is kept within a given number of 293 octaves around the given center. 294 295 @type coords: list of 2-tuples 296 @param coords: (x,y) coordinates 297 @type center: 3-tuple 298 @param center: point that defines the center of the range of 299 pitches within which the output coordinates will lie 300 @type pitch_range: int 301 @param pitch_range: width of the range of pitches, as an integer 302 number of octaves. 303 304 """ 305 center_pitch = tonal_space_pitch(center) 306 half_range = float(pitch_range) / 2 307 # Work out the bounds of the allowed pitch range 308 bottom = center_pitch / (2**half_range) 309 top = center_pitch * (2**half_range) 310 311 coords3d = [] 312 for (x,y) in coords: 313 z = 0 314 while tonal_space_pitch((x,y,z)) > top: 315 z -= 1 316 while tonal_space_pitch((x,y,z)) < bottom: 317 z += 1 318 coords3d.append((x,y,z)) 319 return coords3d
320
321 -def root_to_et_coord(root):
322 """ 323 Given a ET root as an integer in the range 0 <= r < 12, returns the 324 2D ET coordinate in the range (0,0) <= (x,y) < (4,3) that that 325 root has in the 4x3 ET space. 326 327 If the root is not within that range, it will be taken mod 12. 328 329 """ 330 # The map looks like this: 331 # 8 3 10 5 332 # 4 11 6 1 333 # 0 7 2 9 334 root_map = { 335 0 : (0,0), 336 1 : (3,1), 337 2 : (2,0), 338 3 : (1,2), 339 4 : (0,1), 340 5 : (3,2), 341 6 : (2,1), 342 7 : (1,0), 343 8 : (0,2), 344 9 : (3,0), 345 10 : (2,2), 346 11 : (1,1) 347 } 348 return root_map[root % 12]
349