def from_shorthand(note, interval, up=True): """Returns the note on interval up or down. Example: {{{ >>> from_shorthand(\"A\", \"b3\") 'C' >>> from_shorthand(\"D\", \"2\") 'E' >>> from_shorthand(\"E\", \"2\", False) 'D' }}}""" # warning should be a valid note. if not notes.is_valid_note(note): return False # [shorthand, interval function up, interval function down] shorthand_lookup = [ ['1', major_unison, major_unison], ['2', major_second, minor_seventh], ['3', major_third, minor_sixth], ['4', major_fourth, major_fifth], ['5', major_fifth, major_fourth], ['6', major_sixth, minor_third], ['7', major_seventh, minor_second], ] # Looking up last character in interval in shorthand_lookup and calling that # function. val = False for shorthand in shorthand_lookup: if shorthand[0] == interval[-1]: if up: val = shorthand[1](note) else: val = shorthand[2](note) # warning Last character in interval should be 1-7 if val == False: return False # Collect accidentals for x in interval: if x == '#': if up: val = notes.augment(val) else: val = notes.diminish(val) elif x == 'b': if up: val = notes.diminish(val) else: val = notes.augment(val) else: return val
def from_shorthand(note, interval, up = True): """Returns the note on interval up or down. Example: {{{ >>> from_shorthand("A", "b3") 'C' >>> from_shorthand("D", "2") 'E' >>> from_shorthand("E", "2", False) 'D' }}}""" #warning should be a valid note. if not notes.is_valid_note(note): return False # [shorthand, interval function up, interval function down] shorthand_lookup = [ ["1", major_unison, major_unison], ["2", major_second, minor_seventh], ["3", major_third, minor_sixth], ["4", major_fourth, major_fifth], ["5", major_fifth, major_fourth], ["6", major_sixth, minor_third], ["7", major_seventh, minor_second] ] # Looking up last character in interval in shorthand_lookup # and calling that function. val = False for shorthand in shorthand_lookup: if shorthand[0] == interval[-1]: if up: val = shorthand[1](note) else: val = shorthand[2](note) #warning Last character in interval should be 1-7 if val == False: return False # Collect accidentals for x in interval: if x == "#": if up: val = notes.augment(val) else: val = notes.diminish(val) elif x == "b": if up: val = notes.diminish(val) else: val = notes.augment(val) else: return val
def interval(key, start_note, interval): """Returns the note found at the interval starting from start_note in the given \ key. For example interval('C', 'D', 1) will return 'E'. Will raise a \ !KeyError if the start_note is not a valid note.""" if not notes.is_valid_note(start_note): raise KeyError, "The start note '%s' is not a valid note" % start_note notes_in_key = get_notes(key) for n in notes_in_key: if n[0] == start_note[0]: index = notes_in_key.index(n) return notes_in_key[(index + interval) % 7]
def interval(key, start_note, interval): """Return the note found at the interval starting from start_note in the given key. Raise a KeyError exception if start_note is not a valid note. Example: >>> interval('C', 'D', 1) 'E' """ if not notes.is_valid_note(start_note): raise KeyError("The start note '%s' is not a valid note" % start_note) notes_in_key = keys.get_notes(key) for n in notes_in_key: if n[0] == start_note[0]: index = notes_in_key.index(n) return notes_in_key[(index + interval) % 7]
def from_interval(note, interval, up=True): """Return the note on interval up or down. Valid intervals are: m2, M2,...,P4, aug4, dim5 Examples: >>> from_interval('A', 'm3') 'C' >>> from_interval('D', 'M2') 'E' >>> from_interval('E', 'M2', False) 'D' """ # warning should be a valid note. if not notes.is_valid_note(note): return False #{ key : [interval_function_up, interval_function_down]} shorthand_lookup = { 'm2' : (minor_second, major_seventh), 'M2' : (major_second, minor_seventh), 'm3' : (minor_third, major_sixth), 'M3' : (major_third, minor_sixth), 'P4' : (perfect_fourth, perfect_fifth), 'aug4': (augmented_fourth, augmented_fifth), '+4' : (augmented_fourth, augmented_fifth), 'dim5': (diminished_fifth, diminished_fourth), 'P5' : (perfect_fifth, perfect_fourth), 'm6' : (minor_sixth, major_third), 'M6' : (major_sixth, minor_third), 'm7' : (minor_seventh, major_second), 'M7' : (major_seventh, minor_second), } # Looking up interval in shorthand_lookup and call that function. val = False if shorthand_lookup.has_key(interval): if up: val = shorthand_lookup[interval][0](note) else: val = shorthand_lookup[interval][1](note) else: raise ValueError('Invalid transposition interval "{}"'.format(interval)) return val
def get_notes(key): """Returns an ordered list of the notes in this key. For example: if the key is \ set to 'F', this function will return `['F', 'G', 'A', 'Bb', 'C', 'D', \ 'E']`. Exotic or ridiculous keys like 'C####' or even 'Gbb##bb#b##' will \ work; Note however that the latter example will also get cleaned up to 'G'. \ This function will raise an !NoteFormatError if the key isn't recognised""" # check cache global key_dict if _key_cache.has_key(key): return _key_cache[key] if not notes.is_valid_note(key): raise NoteFormatError, "Unrecognised format for key '%s'" % key fifth_index = notes.fifths.index(key[0]) result = [] # fifth_index = 0 is a special case. It's the key of F and needs Bb instead # of B included in the result. if fifth_index != 0: result.append(notes.fifths[(fifth_index - 1) % 7] + key[1:]) for x in notes.fifths[fifth_index:]: result.append(x + key[1:]) for x in notes.fifths[:fifth_index - 1]: result.append(x + key[1:] + '#') else: for x in notes.fifths[0:6]: result.append(x + key[1:]) result.append('Bb' + key[1:]) result.sort() # Remove redundant #'s and b's from the result result = map(notes.remove_redundant_accidentals, result) tonic = result.index(notes.remove_redundant_accidentals(key)) result = result[tonic:] + result[:tonic] # Save result to cache _key_cache[key] = result return result
def from_shorthand(shorthand_string, slash=None): """Take a chord written in shorthand and return the notes in the chord. The function can recognize triads, sevenths, sixths, ninths, elevenths, thirteenths, slashed chords and a number of altered chords. The second argument should not be given and is only used for a recursive call when a slashed chord or polychord is found. See http://tinyurl.com/3hn6v8u for a nice overview of chord patterns. Examples: >>> from_shorthand('Amin') ['A', 'C', 'E'] >>> from_shorthand('Am/M7') ['A', 'C', 'E', 'G#'] >>> from_shorthand('A') ['A', 'C#', 'E'] >>> from_shorthand('A/G') ['G', 'A', 'C#', 'E'] >>> from_shorthand('Dm|G') ['G', 'B', 'D', 'F', 'A'] Recognised abbreviations: the letters "m" and "M" in the following abbreviations can always be substituted by respectively "min", "mi" or "-" and "maj" or "ma". Example: >>> from_shorthand('Amin7') == from_shorthand('Am7') True Triads: 'm', 'M' or '', 'dim' Sevenths: 'm7', 'M7', '7', 'm7b5', 'dim7', 'm/M7' or 'mM7' Augmented chords: 'aug' or '+', '7#5' or 'M7+5', 'M7+', 'm7+', '7+' Suspended chords: 'sus4', 'sus2', 'sus47' or '7sus4', 'sus', '11', 'sus4b9' or 'susb9' Sixths: '6', 'm6', 'M6', '6/7' or '67', '6/9' or '69' Ninths: '9' or 'add9', 'M9', 'm9', '7b9', '7#9', 'm7b9' Elevenths: '11' or 'add11', '7#11', 'm11' Thirteenths: '13' or 'add13', 'M13', 'm13' Altered chords: '7b5', '7b9', '7#9', '67' or '6/7' Special: '5', 'NC', 'hendrix' """ # warning reduce?? if type(shorthand_string) == list: res = [] for x in shorthand_string: res.append(from_shorthand(x)) return res if shorthand_string in ['NC', 'N.C.']: return [] # Shrink shorthand_string to a format recognised by chord_shorthand shorthand_string = shorthand_string.replace('min', 'm') shorthand_string = shorthand_string.replace('mi', 'm') shorthand_string = shorthand_string.replace('-', 'm') shorthand_string = shorthand_string.replace('maj', 'M') shorthand_string = shorthand_string.replace('ma', 'M') # Get the note name if not notes.is_valid_note(shorthand_string[0]): raise NoteFormatError, "Unrecognised note '%s' in chord '%s'"\ % (shorthand_string[0], shorthand_string) name = shorthand_string[0] # Look for accidentals for n in shorthand_string[1:]: if n == '#': name += n elif n == 'b': name += n else: break # Look for slashes and polychords '|' slash_index = -1 s = 0 rest_of_string = shorthand_string[len(name):] for n in rest_of_string: if n == '/': slash_index = s elif n == '|': # Generate polychord return from_shorthand(shorthand_string[:len(name) + s], from_shorthand(shorthand_string[len(name) + s + 1:])) s += 1 # Generate slash chord if slash_index != -1 and rest_of_string not in ['m/M7', '6/9', '6/7']: res = shorthand_string[:len(name) + slash_index] return from_shorthand(shorthand_string[:len(name) + slash_index], shorthand_string[len(name) + slash_index + 1:]) shorthand_start = len(name) short_chord = shorthand_string[shorthand_start:] if chord_shorthand.has_key(short_chord): res = chord_shorthand[short_chord](name) if slash != None: # Add slashed chords if type(slash) == str: if notes.is_valid_note(slash): res = [slash] + res else: raise NoteFormatError, \ "Unrecognised note '%s' in slash chord'%s'" % (slash, slash + shorthand_string) elif type(slash) == list: # Add polychords r = slash for n in res: if n != r[-1]: r.append(n) return r return res else: raise FormatError, 'Unknown shorthand: %s' % shorthand_string
def from_shorthand(shorthand_string, slash = None): """Takes a chord written in shorthand and returns the notes in the \ chord. The function can recognize triads, sevenths, sixths, ninths, elevenths, \ thirteenths, slashed chords and a number of altered chords. \ The second argument should not be given and is only used for a recursive call \ when a slashed chord or polychord is found. See [http://en.wikibooks.org/wiki/Music_Theory/Complete_List_of_Chord_Patterns Wikibooks] for a nice overview of chord patterns. Example: {{{ >>> from_shorthand("Amin") ["A", "C", "E"] >>> from_shorthand("Am/M7") ["F", "Ab", "C", "E"] >>> from_shorthand("A") ["A", "C#", "E"] >>> from_shorthand("A/G") ["G", "A", "C#", "E"] >>> from_shorthand("Dm|G") ["G", "B", "D", "F", "A"] }}} Recognised abbreviations: the letters `m` and `M` in the following abbreviations \ can always be substituted by respectively `min`, `mi` or `-` and `maj` or `ma` (eg. \ `from_shorthand("Amin7") == from_shorthand("Am7")`, etc.). * Triads: *'m'*, *'M'* or *''*, *'dim'*. * Sevenths: *'m7'*, *'M7'*, *'7'*, *'m7b5'*, *'dim7'*, *'m/M7'* or *'mM7'* * Augmented chords: *'aug'* or *'+'*, *'7#5'* or *'M7+5'*, *'M7+'*, *'m7+'*, *'7+'* * Suspended chords: *'sus4'*, *'sus2'*, *'sus47'*, *'sus'*, *'11'*, *'sus4b9'* or *'susb9'* * Sixths: *'6'*, *'m6'*, *'M6'*, *'6/7'* or *'67'*, *6/9* or *69* * Ninths: *'9'*, *'M9'*, *'m9'*, *'7b9'*, *'7#9'* * Elevenths: *'11'*, *'7#11'*, *'m11'* * Thirteenths: *'13'*, *'M13'*, *'m13'* * Altered chords: *'7b5'*, *'7b9'*, *'7#9'*, *'67'* or *'6/7'* * Special: *'5'*, *'NC'*, *'hendrix'* """ #warning reduce?? if type(shorthand_string) == list: res = [] for x in shorthand_string: res.append(from_shorthand(x)) return res if shorthand_string in ["NC", "N.C."]: return [] # Shrink shorthand_string to a format recognised by chord_shorthand shorthand_string = shorthand_string.replace('min', 'm') shorthand_string = shorthand_string.replace('mi', 'm') shorthand_string = shorthand_string.replace('-', 'm') shorthand_string = shorthand_string.replace('maj', 'M') shorthand_string = shorthand_string.replace('ma', 'M') # Get the note name if not notes.is_valid_note(shorthand_string[0]): raise NoteFormatError, "Unrecognised note '%s' in chord '%s'" % \ (shorthand_string[0], shorthand_string) name = shorthand_string[0] # Look for accidentals for n in shorthand_string[1:]: if n == '#': name += n elif n == 'b': name += n else: break # Look for slashes and polychords '|' slash_index = -1 s = 0 rest_of_string = shorthand_string[len(name):] for n in rest_of_string: if n == '/': slash_index = s elif n == '|': # Generate polychord return from_shorthand(shorthand_string[:len(name) + s], \ from_shorthand(shorthand_string[len(name) + s + 1:])) s += 1 # Generate slash chord if slash_index != -1 and rest_of_string not in ["m/M7", "6/9", "6/7"]: res = shorthand_string[:len(name) + slash_index] return from_shorthand(shorthand_string[:len(name) + slash_index], \ shorthand_string[len(name) + slash_index + 1:]) shorthand_start = len(name) # Return chord short_chord = shorthand_string[shorthand_start:] if chord_shorthand.has_key(short_chord): res = chord_shorthand[short_chord](name) if slash != None: # Add slashed chords if type(slash) == str: if notes.is_valid_note(slash): res = [slash] + res else: raise NoteFormatError, \ "Unrecognised note '%s' in slash chord'%s'" %\ (slash, slash + shorthand_string) # Add polychords elif type(slash) == list: r = slash for n in res: if n != r[-1]: r.append(n) return r return res else: raise FormatError, "Unknown shorthand: %s" % shorthand_string
def from_shorthand(shorthand_string, slash=None): """Take a chord written in shorthand and return the notes in the chord. The function can recognize triads, sevenths, sixths, ninths, elevenths, thirteenths, slashed chords and a number of altered chords. The second argument should not be given and is only used for a recursive call when a slashed chord or polychord is found. See http://tinyurl.com/3hn6v8u for a nice overview of chord patterns. Examples: >>> from_shorthand('Amin') ['A', 'C', 'E'] >>> from_shorthand('Am/M7') ['A', 'C', 'E', 'G#'] >>> from_shorthand('A') ['A', 'C#', 'E'] >>> from_shorthand('A/G') ['G', 'A', 'C#', 'E'] >>> from_shorthand('Dm|G') ['G', 'B', 'D', 'F', 'A'] Recognised abbreviations: the letters "m" and "M" in the following abbreviations can always be substituted by respectively "min", "mi" or "-" and "maj" or "ma". Example: >>> from_shorthand('Amin7') == from_shorthand('Am7') True Triads: 'm', 'M' or '', 'dim' Sevenths: 'm7', 'M7', '7', 'm7b5', 'dim7', 'm/M7' or 'mM7' Augmented chords: 'aug' or '+', '7#5' or 'M7+5', 'M7+', 'm7+', '7+' Suspended chords: 'sus4', 'sus2', 'sus47' or '7sus4', 'sus', '11', 'sus4b9' or 'susb9' Sixths: '6', 'm6', 'M6', '6/7' or '67', '6/9' or '69' Ninths: '9' or 'add9', 'M9', 'm9', '7b9', '7#9' Elevenths: '11' or 'add11', '7#11', 'm11' Thirteenths: '13' or 'add13', 'M13', 'm13' Altered chords: '7b5', '7b9', '7#9', '67' or '6/7' Special: '5', 'NC', 'hendrix' """ # warning reduce?? if type(shorthand_string) == list: res = [] for x in shorthand_string: res.append(from_shorthand(x)) return res if shorthand_string in ['NC', 'N.C.']: return [] # Shrink shorthand_string to a format recognised by chord_shorthand shorthand_string = shorthand_string.replace('min', 'm') shorthand_string = shorthand_string.replace('mi', 'm') shorthand_string = shorthand_string.replace('-', 'm') shorthand_string = shorthand_string.replace('maj', 'M') shorthand_string = shorthand_string.replace('ma', 'M') # Get the note name if not notes.is_valid_note(shorthand_string[0]): raise NoteFormatError, "Unrecognised note '%s' in chord '%s'"\ % (shorthand_string[0], shorthand_string) name = shorthand_string[0] # Look for accidentals for n in shorthand_string[1:]: if n == '#': name += n elif n == 'b': name += n else: break # Look for slashes and polychords '|' slash_index = -1 s = 0 rest_of_string = shorthand_string[len(name):] for n in rest_of_string: if n == '/': slash_index = s elif n == '|': # Generate polychord return from_shorthand(shorthand_string[:len(name) + s], from_shorthand(shorthand_string[len(name) + s + 1:])) s += 1 # Generate slash chord if slash_index != -1 and rest_of_string not in ['m/M7', '6/9', '6/7']: res = shorthand_string[:len(name) + slash_index] return from_shorthand(shorthand_string[:len(name) + slash_index], shorthand_string[len(name) + slash_index + 1:]) shorthand_start = len(name) short_chord = shorthand_string[shorthand_start:] if chord_shorthand.has_key(short_chord): res = chord_shorthand[short_chord](name) if slash != None: # Add slashed chords if type(slash) == str: if notes.is_valid_note(slash): res = [slash] + res else: raise NoteFormatError, \ "Unrecognised note '%s' in slash chord'%s'" % (slash, slash + shorthand_string) elif type(slash) == list: # Add polychords r = slash for n in res: if n != r[-1]: r.append(n) return r return res else: raise FormatError, 'Unknown shorthand: %s' % shorthand_string
def from_shorthand(shorthand_string, slash=None): """Takes a chord written in shorthand and returns the notes in the chord. The \ function can recognize triads, sevenths, sixths, ninths, elevenths, \ thirteenths, slashed chords and a number of altered chords. The second \ argument should not be given and is only used for a recursive call when a \ slashed chord or polychord is found. See \ [http://en.wikibooks.org/wiki/Music_Theory/Complete_List_of_Chord_Patterns \ Wikibooks] for a nice overview of chord patterns. Example: {{{ >>> from_shorthand(\"Amin\") [\"A\", \"C\", \"E\"] >>> from_shorthand(\"Am/M7\") [\"F\", \"Ab\", \"C\", \"E\"] >>> from_shorthand(\"A\") [\"A\", \"C#\", \"E\"] >>> from_shorthand(\"A/G\") [\"G\", \"A\", \"C#\", \"E\"] >>> from_shorthand(\"Dm|G\") [\"G\", \"B\", \"D\", \"F\", \"A\"] }}} Recognised abbreviations: the letters `m` and `M` in the following \ abbreviations can always be substituted by respectively `min`, `mi` or `-` \ and `maj` or `ma` (eg. `from_shorthand(\"Amin7\") == from_shorthand(\"Am7\")`, \ etc.). * Triads: *'m'*, *'M'* or *''*, *'dim'*. * Sevenths: *'m7'*, *'M7'*, *'7'*, *'m7b5'*, *'dim7'*, *'m/M7'* or \ *'mM7'* * Augmented chords: *'aug'* or *'+'*, *'7#5'* or *'M7+5'*, *'M7+'*, \ *'m7+'*, *'7+'* * Suspended chords: *'sus4'*, *'sus2'*, *'sus47'*, *'sus'*, *'11'*, \ *'sus4b9'* or *'susb9'* * Sixths: *'6'*, *'m6'*, *'M6'*, *'6/7'* or *'67'*, *6/9* or *69* * Ninths: *'9'*, *'M9'*, *'m9'*, *'7b9'*, *'7#9'* * Elevenths: *'11'*, *'7#11'*, *'m11'* * Thirteenths: *'13'*, *'M13'*, *'m13'* * Altered chords: *'7b5'*, *'7b9'*, *'7#9'*, *'67'* or *'6/7'* * Special: *'5'*, *'NC'*, *'hendrix'* """ # warning reduce?? if type(shorthand_string) == list: res = [] for x in shorthand_string: res.append(from_shorthand(x)) return res if shorthand_string in ['NC', 'N.C.']: return [] # Shrink shorthand_string to a format recognised by chord_shorthand shorthand_string = shorthand_string.replace('min', 'm') shorthand_string = shorthand_string.replace('mi', 'm') shorthand_string = shorthand_string.replace('-', 'm') shorthand_string = shorthand_string.replace('maj', 'M') shorthand_string = shorthand_string.replace('ma', 'M') # Get the note name if not notes.is_valid_note(shorthand_string[0]): raise NoteFormatError, "Unrecognised note '%s' in chord '%s'"\ % (shorthand_string[0], shorthand_string) name = shorthand_string[0] # Look for accidentals for n in shorthand_string[1:]: if n == '#': name += n elif n == 'b': name += n else: break # Look for slashes and polychords '|' slash_index = -1 s = 0 rest_of_string = shorthand_string[len(name):] for n in rest_of_string: if n == '/': slash_index = s elif n == '|': # Generate polychord return from_shorthand( shorthand_string[:len(name) + s], from_shorthand(shorthand_string[len(name) + s + 1:])) s += 1 # Generate slash chord if slash_index != -1 and rest_of_string not in ['m/M7', '6/9', '6/7']: res = shorthand_string[:len(name) + slash_index] return from_shorthand(shorthand_string[:len(name) + slash_index], shorthand_string[len(name) + slash_index + 1:]) shorthand_start = len(name) short_chord = shorthand_string[shorthand_start:] if chord_shorthand.has_key(short_chord): res = chord_shorthand[short_chord](name) if slash != None: # Add slashed chords if type(slash) == str: if notes.is_valid_note(slash): res = [slash] + res else: raise NoteFormatError, \ "Unrecognised note '%s' in slash chord'%s'" % (slash, slash + shorthand_string) elif type(slash) == list: # Add polychords r = slash for n in res: if n != r[-1]: r.append(n) return r return res else: raise FormatError, 'Unknown shorthand: %s' % shorthand_string