Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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]
Ejemplo n.º 5
0
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]
Ejemplo n.º 6
0
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]
Ejemplo n.º 7
0
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]
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
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