Interval
This page details the Interval class and all its methods.
Creating Intervals
Section titled “Creating Intervals”There are three main ways to create Interval vectors from scratch. Directly, using the class constructor; using strings via the class methods Interval.from_name or Interval.from_spn; or from two Pitch vectors via Interval.between (or, equivalently, by subtracting them: q - p).
Class Constructor
Section titled “Class Constructor”Interval(w: int, h: int)Internally an interval is defined as some number of whole steps and some number of half steps from the unison. You can directly initialise an Interval like so:
m = Interval(5, 2) # coordinates for an octave
m.w # 5m.h # 2from_name
Section titled “from_name”Interval.from_name(name: str) -> IntervalIt’s often more convenient to initialise an Interval from a standard interval name as a string. Interval.from_name parses such a string, raising a ValueError if it is not a valid interval name:
m = Interval.from_name("P8")
m.w # 5m.h # 2from_spn
Section titled “from_spn”Interval.from_spn(ps: str, qs: str) -> IntervalInitialises an interval from two note names specified as SPN strings. Measures the distance from the first Pitch to the second.
m = Interval.from_spn("C4", "C5")
m.w # 5m.h # 2between
Section titled “between”Interval.between(p: Pitch, q: Pitch) -> IntervalMeasures the distance from the first Pitch to the second pitch. That is, Interval.between(p, q) returns the vector — which you can also write literally in Python as q - p:
p = SPN.to_pitch("C4")q = SPN.to_pitch("C5")m = Interval.between(p, q) # or: m = q - p
m.w # 5m.h # 2Common Queries
Section titled “Common Queries”The following properties and methods extract information from an Interval vector.
chroma
Section titled “chroma”Interval.chroma: int # read-only propertyThe signed distance of an Interval from the unison in perfect fifths:
m = Interval.from_name("P5")m.chroma # 1
m = Interval.from_name("M3")m.chroma # 4
m = Interval.from_name("m3")m.chroma # -3is_diatonic
Section titled “is_diatonic”Interval.is_diatonic: bool # read-only propertyAffirms whether a given interval is capable of occurring diatonically (i.e. is found within the major scale).
m = Interval.from_name("P5")m.is_diatonic # True
m = Interval.from_name("A4")m.is_diatonic # True
m = Interval.from_name("A6")m.is_diatonic # Falseis_tonal
Section titled “is_tonal”Interval.is_tonal: bool # read-only propertyAffirms whether a given interval is capable of occurring in a tonal context, whether diatonically or chromatically (i.e. whether it is formed between notes that could be within a half step’s distance of a shared diatonic scale).
m = Interval.from_name("P5")m.is_tonal # True
m = Interval.from_name("A6")m.is_tonal # True
m = Interval.from_name("ddd6")m.is_tonal # Falsequality
Section titled “quality”Interval.quality: int # read-only propertyThe quality component of an Interval vector’s standard name, expressed as a signed integer.
- 0 means perfect.
- +1 means major.
- -1 means minor.
- +2 means augmented.
- -2 means diminished.
- etc. for any arbitrary quality.
m = Interval.from_name("P5")m.quality # 0
m = Interval.from_name("M3")m.quality # 1
m = Interval.from_name("m3")m.quality # -1
m = Interval.from_name("A6")m.quality # 2stepspan
Section titled “stepspan”Interval.stepspan: int # read-only propertyThe “stepspan” of an Interval is the number of diatonic steps it contains, where 0 is the unison, 1 is a generic second and so on. Essentially one less than the number conventionally used to describe the generic size of intervals.
m = Interval.from_name("M6")m.stepspan # 5
m = Interval.from_name("P8")m.stepspan # 7Interval.pc7: int # read-only propertyThe 0-indexed 7-tone pitch class of an Interval. Essentially its size in steps, modulo 7.
m = Interval.from_name("M6")m.pc7 # 5
m = Interval.from_name("M13")m.pc7 # also 5Interval.pc12: int # read-only propertyThe 12-tone pitch class interval of an Interval vector.
m = Interval.from_name("P5")m.pc12 # 7Interval.name: str # read-only propertyThe standard name of an Interval rendered as a string.
m = Interval.from_name("d5")m.name # "d5"simple
Section titled “simple”Interval.simple: Interval # read-only propertyThe size of an interval, reduced until it is smaller than an octave. This includes the octave, which is considered a compound unison here.
m = Interval.from_name("M17")m.simple.name # "M3"negative
Section titled “negative”Interval.negative: Interval # read-only propertyThe flipped version of an Interval. An ascending major 3rd will become a descending major 3rd. Or for vertical intervals, voices will exchange places. Equivalent to the unary minus operator: -m.
m = Interval.from_name("M3")p = SPN.to_pitch("C4")
p.transpose_real(m) # E4p.transpose_real(m.negative) # Ab3p + -m # Ab3 (equivalent)is_equal
Section titled “is_equal”Interval.is_equal(m: Interval) -> boolReturns True if and only if the passed in Interval vector and the current vector are identical. Note that this does not mean “are enharmonically equivalent in 12TET”, or “have the same chroma”: they must literally hold the same coordinates.
The == operator behaves identically, and is usually more convenient in Python.
m = Interval.from_name("A4")n = Interval.from_name("d5")
m.is_equal(n) # False
n = Interval.from_name("A11")m.is_equal(n) # False
n = Interval.from_name("A4")m.is_equal(n) # Truem == n # True (equivalent)is_enharmonic
Section titled “is_enharmonic”Interval.is_enharmonic(m: Interval, edo: int = 12) -> boolReturns True if the passed in Interval is enharmonic to the current Interval in the passed in EDO tuning. If no second argument is passed this method defaults to 12TET. Intervals will still register as enharmonic if one is simple and the other is compound.
m = Interval.from_name("m7")n = Interval.from_name("A6")
m.is_enharmonic(n) # True (in 12TET)m.is_enharmonic(n, 31) # False (in 31EDO)
m = Interval.from_name("AAAA6")n = Interval.from_name("P8")
m.is_enharmonic(n) # False (in 12TET)m.is_enharmonic(n, 31) # True (in 31EDO)Transformations
Section titled “Transformations”The following methods produce a new vector from the current Interval. None of them mutate the original object.
Interval.add(m: Interval) -> IntervalAdds two Interval vectors together. Equivalent to the + operator.
m = Interval.from_name("M3")n = Interval.from_name("m3")
m.add(n) # P5m + n # P5 (equivalent)subtract
Section titled “subtract”Interval.subtract(m: Interval) -> IntervalSubtracts two Interval vectors. Equivalent to the - operator.
m = Interval.from_name("P5")n = Interval.from_name("m3")
m.subtract(n) # M3m - n # M3 (equivalent)Interval.times(x: int) -> IntervalMultiplies an Interval vector by a scalar. Equivalent to the * operator.
m = Interval.from_name("P5")
m.times(2) # M9m * 2 # M9 (equivalent)Operators
Section titled “Operators”Interval vectors support natural arithmetic through Python’s operators:
| Expression | Result | Equivalent to |
|---|---|---|
m + n | Interval | m.add(n) |
m - n | Interval | m.subtract(n) |
-m | Interval | m.negative |
m * x / x * m | Interval | m.times(x) |
m == n | bool | m.is_equal(n) |
p + m / m + p | Pitch | p.transpose_real(m) |
m = Interval.from_name("M3")
m + m # A5 -- three major thirds only close the octave in 12TET,m * 3 # A7 so their exact sum is an augmented 7th (e.g. C→B#)Multiplying by a non-integer scalar raises a TypeError: intervals live on an integer lattice.
Ranges
Section titled “Ranges”range.diatonic
Section titled “range.diatonic”Interval.range.diatonic(from_: Interval | None = None, to: Interval | None = None) -> Iterator[Interval]Generates a range of Interval vectors between the two boundary Intervals passed-in, such that all intervals found diatonically occur.
If no boundary Intervals are specified, defaults to P1 - P8.
m = Interval.from_name("P1")n = Interval.from_name("M9")for k in Interval.range.diatonic(m, n): print(k.name)# P1 m2 M2 m3 M3 P4 d5 A4 P5 m6 M6 m7 M7 P8 m9range.melodic
Section titled “range.melodic”Interval.range.melodic() -> Iterator[Interval]Generates all the intervals traditionally considered “easily singable”. Essentially no dissonant leaps or augmented/diminished intervals, nor leaps bigger than an octave.
for m in Interval.range.melodic(): print(m.name)# P1 m2 M2 m3 M3 P4 P5 m6 M6 P8