The pitch module

Classes and functions to deal with LilyPond pitches.

A pitch consists of a step (note, the index in the global default scale) and an alteration, which is a rational value (fraction or floating point) in whole tones. The notes 0..6 correspond with the usual “white keys” C, D, E, F, G, A, B; a sharp is represented by a +0.5 alteration value, and a flat by a -0.5 value.

The octave of a pitch is 0 for the octave starting at middle C, just like LilyPond handles the octave.

All functions and classes in this module, and also in the key module, allow specifying a different global default scale (set in the MAJOR_SCALE module constant), to theoretically support other tone systems, but that will probably almost never be necessary.

MAJOR_SCALE = (0, 1, 2, 2.5, 3.5, 4.5, 5.5)

Major scale: C D E F G A B, with the default pitch offset from the starting C in whole tones.

MAJOR_FLATS = (1.5, 5)

Which pitch values get a flat by default instead of a sharp when converting a MIDI key number to a pitch.

class Pitch(octave, note, alter)[source]

Bases: object

A pitch with octave, note, and alter attributes.

The attributes may be manipulated directly, and have the same contents and meaning as the three values in LilyPond’s (ly:make-pitch octave note alter) construct.

The octave is an integer where 0 stands for the octave containing “middle C” (with one apostrophe in LilyPond’s format). The note is an integer in the 0..6 range, where 0 stands for C; the alter is an integer, float or fraction denoting the alteration in whole tones, where all pitch languages support the values -1, -0.5, 0, 0.5, 1, and some languages also support semi, three-quarter alterations like 0.25 (i.e. Fraction(1, 4)), or even other alterations.

Pitches compare equal when their attributes are the same, and also support the >, <, >= and <= operators. These operators compare on octave first, then note, then alter.

format(pitch) returns always the dutch notation (or a question mark if there’s no known name for the note, alter combination), but you can use PitchProcessor to read/write pitch names in all LilyPond languages.

copy()[source]

Return a new Pitch with our attributes.

to_midi(scale=None)[source]

Return the MIDI key number for this pitch.

classmethod from_midi(key, scale=None, flats=None)[source]

Return a Pitch from the MIDI key value.

All altered notes get a sharp, unless a pitch value is listed in the flats parameter. By default, the pitch values 1.5 and 5 get a flat, resulting in an e-flat instead of d-sharp and a b-flat instead of an a-sharp.

An example:

>>> from quickly.pitch import Pitch
>>> Pitch.from_midi(60)
<Pitch note=0, alter=0, octave=1 (c')>
>>> Pitch.from_midi(61)
<Pitch note=0, alter=0.5, octave=1 (cis')>
>>> Pitch.from_midi(70)
<Pitch note=6, alter=-0.5, octave=1 (bes')>
>>> Pitch.from_midi(70, flats=())
<Pitch note=5, alter=0.5, octave=1 (ais')>

A more powerful way to convert MIDI key numbers to pitches is in the KeySignature class.

make_absolute(prev_pitch, default_octave=-1, scale=None)[source]

Make ourselves absolute, i.e. set our octave from prev_pitch.

The default octave is the octave a pitch name without octave indication by itself has (-1 by default).

make_relative(prev_pitch, default_octave=-1, scale=None)[source]

Make ourselves relative, i.e. change our octave from prev_pitch.

The default octave is the octave a pitch name without octave indication by itself has (-1 by default).

class PitchProcessor(language=None)[source]

Bases: object

Read and write pitch names in all LilyPond languages.

The language to use by default can be given on instantiation or set in the language attribute. Some languages have multiple pitch names for the same note; using the prefer_ attributes you can control which style is chosen when writing the pitch name.

prefer_long = False

Prefer long names in english, e.g. c-sharpsharp above css

prefer_accented = False

Prefer above re (in francais)

prefer_x = False

Prefer dox above doss, cx above css, etc in enspanol, english, francais

prefer_double_s = False

Prefer ss above s inside note names in norsk

prefer_classic = True

Prefer es above ees and as above aes (in nederlands, norsk)

prefer_deprecated = False

Prefer names marked as deprecated

property language

The language to use (default: "nederlands").

Deleting this attribute sets it back to "nederlands".

Raises a KeyError if the language you try to set does not exist. Valid languages are: "arabic", "bagpipe", "catalan", "català", "deutsch", "english", "espanol", "español", "français", "italiano", "nederlands", "norsk", "persian", "portugues", "português", "suomi", "svenska", "vlaams".

Do not modify the language between a read_node() and write_node() operation on the same node. For translation of pitch names, use two PitchProcessors.

pitch(name)[source]

Return a Pitch for the specified note name.

Raises a KeyError if the language does not know the pitch name, or when the language name is unknown.

For example:

>>> from quickly.pitch import PitchProcessor
>>> p = PitchProcessor()
>>> p.read('cis')
<Pitch octave=-1, note=0, alter=0.5 (cis)>
name_octave(pitch)[source]

Return a two-tuple (name, octave) for the Pitch.

The name is the note name, the octave is the number of , (if negative) or ' that still need to be added.

Raises a KeyError if the language does not contain a pitch name.

to_string(pitch)[source]

Return a string representing the pitch.

Raises a KeyError if the language does not contain a pitch name.

For example:

>>> from quickly.pitch import PitchProcessor
>>> p = PitchProcessor()
>>> p.write(Pitch(-1, 0, 0))
'c'
>>> p.write(Pitch(0, 4, 1))
"gisis'"
>>> p.language = 'english'
>>> p.write(Pitch(0, 4, 1))
"gss'"
>>> p.prefer_long = True
>>> p.write(Pitch(0, 4, 1))
"g-sharpsharp'"
read_node(node)[source]

Return a Pitch, initialized from the node.

The node is a Note, positioned PitchedRest or any other Pitchable. For example:

>>> from quickly.pitch import PitchProcessor
>>> from quickly.dom import lily
>>> n = lily.Note('re')
>>> p = PitchProcessor('français')
>>> p.read_node(n)
<Pitch octave=-1, note=1, alter=0 (d)>

The octave handling might be a little confusing at first sight: A Note node without octave characters has octave 0, while the pitch has octave -1. This is because, just like in LilyPond, the pitch name itself carries the octave -1, and the octave count of the node is added to it to get the resulting octave of the actual pitch:

>>> n.octave                    # number of ' or ,
0
>>> p.read_node(n).octave       # actual octave
-1
write_node(node, pitch)[source]

Write the Pitch’s note, alter and octave to the node.

The node is a Note, positioned PitchedRest or any other Pitchable. Example:

>>> from quickly.pitch import Pitch, PitchProcessor
>>> from quickly.dom import lily
>>> n = lily.Note('c')
>>> p = PitchProcessor()
>>> p.write_node(n, Pitch(2, 1, 0.5))
>>> n.dump()
<lily.Note 'dis' (1 child)>
 ╰╴<lily.Octave 3>
process(node, write=True)[source]

Return a context manager that yields a Pitch when entered.

The node is a Note, positioned PitchedRest or any other Pitchable. You can manipulate the Pitch, and when done, the node will be updated if the pitch was changed. An example:

>>> from quickly.pitch import PitchProcessor
>>> from quickly.dom import lily
>>> n = lily.Note('c')
>>> p = PitchProcessor()
>>> with p.process(n) as pitch:
...     pitch.note += 2
...     pitch.alter = 0.5
...     pitch.octave += 1
...
>>> n.write()
"eis'"
>>> n.dump()
<lily.Note 'eis' (1 child)>
 ╰╴<lily.Octave 1>

If you set the write parameter to False on invocation, the pitch changes will not be written back to the DOM node, this enables you to e.g. apply changes only within a certain range.

pitchable(pitch, cls=None)[source]

Return a new Pitchable element for the pitch.

By default, a Note is returned, but you may specify any Pitchable subclass.

>>> from quickly.pitch import *
>>> p = PitchProcessor('nederlands')
>>> n = p.pitchable(Pitch(2, 3, -.25))
>>> n.dump()
<lily.Note 'feh' (1 child)>
 ╰╴<lily.Octave 3>
>>> n.write()
"feh'''"
find_language(node)[source]

Search backwards from node to find the last set language.

If an \include command is found that names a language file, or a \language command with a valid language, that language is set.

follow_language(nodes)[source]

Iterate over the DOM nodes and follow language changes.

Yield every node, except for lily.Language or lily.Include if a language name is included. Sets the language attribute according to the \language or \include command.

distill_preferences(names)[source]

Iterate over the names and try to distill the preferred style.

Adjust the preferences based on the encountered pitch names.

This can be used to analyze existing music and use the same pitch name preferences for newly entered music.

The names iterable may be a set but also an ordered sequence or generator. If a name is encountered that is a language name, that language is followed to test following pitch names. (The language attribute is not changed.)

octave_to_string(n)[source]

Convert a numeric value to an octave notation.

The octave notation consists of zero or more ' or ,. The octave 0 returns the empty string.

octave_from_string(octave)[source]

Convert an octave string to a numeric value.

'' is converted to 2, , to -1. The empty string gives 0.

determine_language(names)[source]

Yield the language names that have all the specified pitch names.

This can be used to auto-determine the language of music if the language name somehow is not set in a file. Just harvest all the pitch names and call this function. The pitch names "r", "R", "s" and "q" are ignored. For example:

>>> from quickly.pitch import determine_language
>>> list(determine_language(['c', 'd', 'es', 'fis', 'bis']))
['nederlands']
>>> list(determine_language(['c', 'do']))
[]  # ambiguous