Intervals¶
Intervals describe the difference between two frequency representations, for example the difference between C4 and E4 (a major third interval). The main purpose of the interval primitive is to define the span and harmonic function of a transposition.
Xenharmlib distinguishes between intervals that are enharmonically equal to preserve their harmonic function, so an augmented second and a minor third in Western notation produce different results with regard to transposition, even though they describe the same difference in pitch:
from xenharmlib import WesternNotation
western = WesternNotation()
A2 = western.shorthand_interval('A', 2)
m3 = western.shorthand_interval('m', 3)
result_a = western.note('D', 4).transpose(A2)
result_b = western.note('D', 4).transpose(m3)
print(result_a)
print(result_b)
print(result_a == result_b)
WesternNote(E#, 4)
WesternNote(F, 4)
True
The interval primitive also supports various arithmetic operators and transformation functions (for example inversion, compound-to-simple-mapping, intervals class normalization, etc). The following chapter will look at intervals more in-depth and give examples on various origin contexts.
Deriving Intervals¶
There are two way to derive an interval from two frequency representations:
One is the interval()
method of the origin context, the other is the
interval() method found directly
on the pitch or note:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
interval_1 = edo31.interval(p10, p18)
interval_2 = p10.interval(p18)
print(interval_1)
print(interval_2)
EDOPitchInterval(8, 31-EDO)
EDOPitchInterval(8, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
interval_1 = limit5.interval(C0, G0)
interval_2 = C0.interval(G0)
print(interval_1)
print(interval_2)
PrimeLimitPitchInterval(3/2, 5-Limit)
PrimeLimitPitchInterval(3/2, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
interval_1 = western.interval(C4, E4)
interval_2 = C4.interval(E4)
print(interval_1)
print(interval_2)
WesternNoteInterval(M, 3)
WesternNoteInterval(M, 3)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
interval_1 = n_edo31.interval(C4, Eup4)
interval_2 = C4.interval(Eup4)
print(interval_1)
print(interval_2)
UpDownNoteInterval(^M, 3, 31-EDO)
UpDownNoteInterval(^M, 3, 31-EDO)
Depending on tuning and notation, there are other direct builder methods available to create intervals. While in every origin context intervals can be derived from two frequency representations, direct methods are highly dependend on context.
Difference-based Construction¶
You can create intervals by providing the pitch difference using the
diff_interval()
method. Depending on the index scheme, this pitch difference can
be an integer or a LatticePoint object:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
P5 = edo31.diff_interval(18)
print(P5)
EDOPitchInterval(18, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
P5 = limit5.diff_interval(limit5.lattice.point((-1, 1, 0)))
print(P5)
PrimeLimitPitchInterval(3/2, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
P5 = western.diff_interval(7)
print(P5)
WesternNoteInterval(P, 5)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
P5 = n_edo31.diff_interval(18)
print(P5)
UpDownNoteInterval(P, 5, 31-EDO)
In notations where there is enharmonic ambiguity (e.g., the Western notation), the result can vary depending on the configured enharmonic strategy of the notation, i.e., a pitch difference of 4 can be interpreted as a m3 interval or an A2 interval. Since there is no way of knowing the harmonic function of an interval given in absolute pitch difference, xenharmlib makes a guess.
For tunings and notations whose pitch differences are represented by lattice
points, xenharmlib also provides the more concise
vec_interval() method:
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
# these two expressions create the same object
P5 = limit5.diff_interval(limit5.lattice.point((-1, 1, 0)))
P5_alt = limit5.vec_interval((-1, 1, 0))
Name-based Construction¶
On the notation layer intervals, can also be created by their shorthand name. In fact, every notation in xenharmlib comes with an interval naming scheme, so - like notes - every interval has a distinct name from which it can be uniquely identified. Details about the interval naming scheme can be obtained from the relevant notation chapter of this documentation.
from xenharmlib import WesternNotation
western = WesternNotation()
P5 = western.shorthand_interval('P', 5)
print(P5)
WesternNoteInterval(P, 5)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
supermajor3 = n_edo31.shorthand_interval('^M', 3)
print(supermajor3)
UpDownNoteInterval(^M, 3, 31-EDO)
Construction Based on Closest Frequency Ratio¶
Origin contexts based on integer indices allow construction based on the
approximation of frequency ratios. Given an arbitrary frequency ratio, the
closest_interval()
method returns the interval of an origin context that is closest to it:
from xenharmlib import EDOTuning
from xenharmlib import FrequencyRatio
edo31 = EDOTuning(31)
interval = edo31.closest_interval(FrequencyRatio(7, 4))
print(interval)
EDOPitchInterval(25, 31-EDO)
from xenharmlib import WesternNotation
from xenharmlib import FrequencyRatio
western = WesternNotation()
interval = western.closest_interval(FrequencyRatio(3, 2))
print(interval)
WesternNoteInterval(P, 5)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
from xenharmlib import FrequencyRatio
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
interval = n_edo31.closest_interval(FrequencyRatio(5, 4))
print(interval)
UpDownNoteInterval(M, 3, 31-EDO)
Simple and Compound¶
In relation to an equivalency interval, intervals can be categorized into “simple intervals” (smaller or equal to the equivalency interval) or “compound intervals” (bigger than the equivalency interval). In the Western system, for example, M2 is a simple interval while M9 is a compound interval.
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
interval = edo31.diff_interval(33)
print(interval.is_simple)
print(interval.is_compound)
False
True
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
interval = limit5.diff_interval(limit5.lattice.point((0, 1, 0)))
print(interval.is_simple)
print(interval.is_compound)
False
True
from xenharmlib import WesternNotation
western = WesternNotation()
interval = western.shorthand_interval('M', 9)
print(interval.is_simple)
print(interval.is_compound)
False
True
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
interval = n_edo31.shorthand_interval('^M', 9)
print(interval.is_simple)
print(interval.is_compound)
False
True
Compound intervals can be reduced to their simple equivalent easily. For example, in the Western system M9 has the simple interval equivalent of M2:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
interval = edo31.diff_interval(33)
simple = interval.to_simple()
print(simple)
EDOPitchInterval(2, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
interval = limit5.diff_interval(limit5.lattice.point((0, 1, 0)))
simple = interval.to_simple()
print(simple)
PrimeLimitPitchInterval(3/2, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
interval = western.shorthand_interval('M', 9)
simple = interval.to_simple()
print(simple)
WesternNoteInterval(M, 2)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
interval = n_edo31.shorthand_interval('^M', 9)
simple = interval.to_simple()
print(simple)
UpDownNoteInterval(^M, 2, 31-EDO)
Direction, Inversion & Absolute¶
Intervals in xenharmlib are directional, meaning there is a difference between ascending and descending intervals. Each ascending interval has its descending counterpart and vice versa. Whether you receive an ascending or descending interval depends on the order of the notes from which an interval is constructed. If the first note is lower than the second, you will receive an ascending interval. If the first note is higher than the second, you will receive a descending interval:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
ascending = p10.interval(p18)
descending = p18.interval(p10)
print(ascending)
print(descending)
EDOPitchInterval(8, 31-EDO)
EDOPitchInterval(-8, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
ascending = C0.interval(G0)
descending = G0.interval(C0)
print(ascending)
print(descending)
PrimeLimitPitchInterval(3/2, 5-Limit)
PrimeLimitPitchInterval(2/3, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
ascending = C4.interval(E4)
descending = E4.interval(C4)
print(ascending)
print(descending)
WesternNoteInterval(M, 3)
WesternNoteInterval(M, -3)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
ascending = C4.interval(Eup4)
descending = Eup4.interval(C4)
print(ascending)
print(descending)
UpDownNoteInterval(^M, 3, 31-EDO)
UpDownNoteInterval(^M, -3, 31-EDO)
An ascending interval can be transformed into a descending interval
(and vice versa) by prefixing the interval object with a minus
(-) sign. For an interval X, we call the object
-X the negation of X, with X + (-X)
always resulting in the unison interval:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
ascending = p10.interval(p18)
descending = -ascending
print(ascending)
print(descending)
print(ascending + descending)
EDOPitchInterval(8, 31-EDO)
EDOPitchInterval(-8, 31-EDO)
EDOPitchInterval(0, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
ascending = C0.interval(G0)
descending = -ascending
print(ascending)
print(descending)
print(ascending + descending)
PrimeLimitPitchInterval(3/2, 5-Limit)
PrimeLimitPitchInterval(2/3, 5-Limit)
PrimeLimitPitchInterval(1, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
ascending = C4.interval(E4)
descending = -ascending
print(ascending)
print(descending)
print(ascending + descending)
WesternNoteInterval(M, 3)
WesternNoteInterval(M, -3)
WesternNoteInterval(P, 1)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
ascending = C4.interval(Eup4)
descending = -ascending
print(ascending)
print(descending)
print(ascending + descending)
UpDownNoteInterval(^M, 3, 31-EDO)
UpDownNoteInterval(^M, -3, 31-EDO)
UpDownNoteInterval(P, 1, 31-EDO)
Interval objects provide the sign
property to inspect the direction of an interval, returning 1 for an
ascending interval, -1 for a descending interval, and 0 for the unison
interval.
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
ascending = p10.interval(p18)
descending = -ascending
unison = p10.interval(p10)
print(ascending.sign)
print(descending.sign)
print(unison.sign)
1
-1
0
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
ascending = C0.interval(G0)
descending = -ascending
unison = C0.interval(C0)
print(ascending.sign)
print(descending.sign)
print(unison.sign)
1
-1
0
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
ascending = C4.interval(E4)
descending = -ascending
unison = C4.interval(C4)
print(ascending.sign)
print(descending.sign)
print(unison.sign)
1
-1
0
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
ascending = C4.interval(Eup4)
descending = -ascending
unison = C4.interval(C4)
print(ascending.sign)
print(descending.sign)
print(unison.sign)
1
-1
0
Intervals support the abs() function that returns the corresponding
ascending interval for a descending interval (or the interval itself if
it is already ascending or unison)
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
descending = p18.interval(p10)
ascending = abs(descending)
print(descending)
print(ascending)
EDOPitchInterval(-8, 31-EDO)
EDOPitchInterval(8, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
descending = G0.interval(C0)
ascending = abs(descending)
print(descending)
print(ascending)
PrimeLimitPitchInterval(2/3, 5-Limit)
PrimeLimitPitchInterval(3/2, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
descending = E4.interval(C4)
ascending = abs(descending)
print(descending)
print(ascending)
WesternNoteInterval(M, -3)
WesternNoteInterval(M, 3)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
descending = Eup4.interval(C4)
ascending = abs(descending)
print(descending)
print(ascending)
UpDownNoteInterval(^M, -3, 31-EDO)
UpDownNoteInterval(^M, 3, 31-EDO)
In contrast, inversion of an interval X is defined as the interval
Y, so that X + Y equals the equivalency interval [1].
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
p10 = edo31.pitch(10)
p18 = edo31.pitch(18)
interval = p10.interval(p18)
inv_interval = interval.inversion()
print(interval)
print(inv_interval)
print(interval + inv_interval)
EDOPitchInterval(8, 31-EDO)
EDOPitchInterval(23, 31-EDO)
EDOPitchInterval(31, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
C0 = limit5.pitch(limit5.lattice.point((0, 0, 0)))
G0 = limit5.pitch(limit5.lattice.point((-1, 1, 0)))
interval = C0.interval(G0)
inv_interval = interval.inversion()
print(interval)
print(inv_interval)
print(interval + inv_interval)
PrimeLimitPitchInterval(3/2, 5-Limit)
PrimeLimitPitchInterval(4/3, 5-Limit)
PrimeLimitPitchInterval(2, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
C4 = western.note('C', 4)
E4 = western.note('E', 4)
interval = C4.interval(E4)
inv_interval = interval.inversion()
print(interval)
print(inv_interval)
print(interval + inv_interval)
WesternNoteInterval(M, 3)
WesternNoteInterval(m, 6)
WesternNoteInterval(P, 8)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
C4 = n_edo31.note('C', 4)
Eup4 = n_edo31.note('^E', 4)
interval = C4.interval(Eup4)
inv_interval = interval.inversion()
print(interval)
print(inv_interval)
print(interval + inv_interval)
UpDownNoteInterval(^M, 3, 31-EDO)
UpDownNoteInterval(vm, 6, 31-EDO)
UpDownNoteInterval(P, 8, 31-EDO)
Identity and Comparison¶
While pitch and note objects are representations of frequencies,
intervals are representations of frequency ratios. Similar to
pitches and notes, two intervals are considered equal if their
frequency_ratio property
is equal. This equality relation works across different origin
contexts:
from xenharmlib import EDOTuning
edo12 = EDOTuning(12)
edo24 = EDOTuning(24)
P5_12 = edo12.diff_interval(7)
P5_24 = edo24.diff_interval(14)
print(P5_12.frequency_ratio)
print(P5_24.frequency_ratio)
print(P5_12 == P5_24)
FrequencyRatio(2**(7/12))
FrequencyRatio(2**(7/12))
True
from xenharmlib import PrimeLimitTuning
limit3 = PrimeLimitTuning(3)
limit5 = PrimeLimitTuning(5)
P5_l3 = limit3.vec_interval((-1, 1))
P5_l5 = limit5.vec_interval((-1, 1, 0))
print(P5_l3.frequency_ratio)
print(P5_l5.frequency_ratio)
print(P5_l3 == P5_l5)
FrequencyRatio(3/2)
FrequencyRatio(3/2)
True
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo24 = EDOTuning(24)
edo48 = EDOTuning(48)
n_edo24 = UpDownNotation(edo24)
n_edo48 = UpDownNotation(edo48)
interval_24 = n_edo24.shorthand_interval('^M', 3)
interval_48 = n_edo48.shorthand_interval('^^M', 3)
print(interval_24.frequency_ratio)
print(interval_48.frequency_ratio)
print(interval_24 == interval_48)
FrequencyRatio(2**(3/8))
FrequencyRatio(2**(3/8))
True
Likewise, comparison operators work by comparing frequency ratios:
from xenharmlib import EDOTuning
edo12 = EDOTuning(12)
P5 = edo12.diff_interval(7)
M3 = edo12.diff_interval(4)
print(M3 < P5)
print(M3 > P5)
True
False
from xenharmlib import PrimeLimitTuning
limit3 = PrimeLimitTuning(3)
limit5 = PrimeLimitTuning(5)
P5 = limit5.vec_interval((-1, 1, 0))
M3pure = limit5.vec_interval((-2, 0, 1))
print(M3pure < P5)
print(M3pure > P5)
True
False
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
P5 = n_edo31.shorthand_interval('P', 5)
M3 = n_edo31.shorthand_interval('M', 3)
print(M3 < P5)
print(M3 > P5)
True
False
Since descending intervals are inverting the frequency ratio of their ascending counterpart (e.g., negating an ascending pure fifth with ratio \(\frac{3}{2}\) we will receive a descending pure fifth with ratio \(\frac{2}{3}\)) descending frequencies are always considered smaller than the unison interval, even if they might be bigger in absolute size:
from xenharmlib import EDOTuning
edo12 = EDOTuning(12)
desc_P5 = edo12.diff_interval(-7)
P1 = edo12.diff_interval(0)
print(desc_P5 < P1)
True
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
desc_P5 = limit5.vec_interval((1, -1, 0))
P1 = limit5.vec_interval((0, 0, 0))
print(desc_P5 < P1)
True
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
desc_P5 = n_edo31.shorthand_interval('P', -5)
P1 = n_edo31.shorthand_interval('P', 1)
print(desc_P5 < P1)
True
Equality/Identity also translates to Python’s built-in set type. If two intervals are considered equal, combining them in a Python set will result in a one-element set:
from xenharmlib import EDOTuning
from xenharmlib import WesternNotation
edo24 = EDOTuning(24)
western = WesternNotation()
interval_a = edo24.diff_interval(14)
interval_b = western.shorthand_interval('P', 5)
# since the second element is equal to the first
# only the first element will be added to the set
print({interval_a, interval_b})
{EDOPitchInterval(14, 24-EDO)}
Subtraction and Addition¶
Intervals support addition and subtraction. The plus (+) operator
stacks two intervals on top of one another, meaning that transposing by the
sum of two intervals is the same as transposing first by the first interval
and then by the second. The minus (-) operator is defined as the
addition of the negation of the second interval, i.e. transposing by
\(X-Y\) is equal to transposing by \(X\) first and then
transposing by \(-Y\)
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
interval_a = edo31.diff_interval(9)
interval_b = edo31.diff_interval(7)
print(interval_a + interval_b)
print(interval_a - interval_b)
EDOPitchInterval(16, 31-EDO)
EDOPitchInterval(2, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
P5 = limit5.diff_interval(limit5.lattice.point((-1, 1, 0)))
M3_pure = limit5.diff_interval(limit5.lattice.point((-2, 0, 1)))
print(P5 + M3_pure)
print(P5 - M3_pure)
PrimeLimitPitchInterval(15/8, 5-Limit)
PrimeLimitPitchInterval(6/5, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
m3 = western.shorthand_interval('m', 3)
M3 = western.shorthand_interval('M', 3)
print(M3 + m3)
print(M3 - m3)
WesternNoteInterval(P, 5)
WesternNoteInterval(A, 1)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
up_M3 = n_edo31.shorthand_interval('^M', 3)
down_m3 = n_edo31.shorthand_interval('vm', 3)
print(up_M3 + down_m3)
print(up_M3 - down_m3)
UpDownNoteInterval(P, 5, 31-EDO)
UpDownNoteInterval(^^A, 1, 31-EDO)
Continued Stacking / Multiplication¶
Intervals can be multiplied by an integer, which is the same as stacking an interval upon itself a number of times. Continued stacking of the same interval has many applications, for example, in the circle of fifths or the generation of scales:
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
P5 = edo31.diff_interval(18)
print(3 * P5)
EDOPitchInterval(54, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
P5 = limit5.diff_interval(limit5.lattice.point((-1, 1, 0)))
print(3 * P5)
PrimeLimitPitchInterval(27/8, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
P5 = western.shorthand_interval('P', 5)
print(3 * P5)
WesternNoteInterval(M, 13)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
up_M3 = n_edo31.shorthand_interval('^M', 3)
print(3 * up_M3)
UpDownNoteInterval(^^^A, 7, 31-EDO)
Interval Class Calculation¶
Similar to pitch classes that represent sets of equivalent pitches, intervals can be sorted into interval classes that represent intervals of the same “intervallic content” or “color”. This equivalency is defined as follows:
Two intervals that only differ in direction belong inside the same class (C1 to G1 has the same color as G1 to C1)
If one interval is the simplified version of another compound interval, they belong inside the same class (C1 to G1 has the same color as C1 to G2 or C1 to G5)
If one interval is the inversion of another, they belong to the same class (C1 to G1 has the same color as G1 to C2)
Each interval class has a normalized representative that is calculated from an interval of the same class as follows:
Calculate the absolute of the interval
Transform the result into a simple interval
Compare the result and its inversion and choose whichever is smaller
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
interval = edo31.diff_interval(-18)
ic = interval.ic_normalized()
print(ic)
EDOPitchInterval(13, 31-EDO)
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
interval = limit5.diff_interval(limit5.lattice.point((0, 1, 0)))
ic = interval.ic_normalized()
print(ic)
PrimeLimitPitchInterval(4/3, 5-Limit)
from xenharmlib import WesternNotation
western = WesternNotation()
interval = western.shorthand_interval('P', 5)
ic = interval.ic_normalized()
print(ic)
WesternNoteInterval(P, 4)
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
interval = n_edo31.shorthand_interval('^M', 9)
ic = interval.ic_normalized()
print(ic)
UpDownNoteInterval(^M, 2, 31-EDO)
Instead of receiving an interval object, you can also get the interval class index (which is the numeric representation used in most posttonal theory textbooks):
from xenharmlib import EDOTuning
edo31 = EDOTuning(31)
interval = edo31.diff_interval(-18)
print(interval.ic_index)
13
from xenharmlib import PrimeLimitTuning
limit5 = PrimeLimitTuning(5)
interval = limit5.diff_interval(limit5.lattice.point((0, 1, 0)))
print(interval.ic_index)
LatticePoint(2, -1, 0)
from xenharmlib import WesternNotation
western = WesternNotation()
interval = western.shorthand_interval('P', 5)
print(interval.ic_index)
5
from xenharmlib import EDOTuning
from xenharmlib import UpDownNotation
edo31 = EDOTuning(31)
n_edo31 = UpDownNotation(edo31)
interval = n_edo31.shorthand_interval('^M', 9)
print(interval.ic_index)
6
Footnotes