Core API documentation

Frequency

Frequencies are the prime substance of pitches. Everything in xenharmlib (and in music, for that matter) ultimately boils down to frequencies and their relations to one another. In this module, we implement a couple of useful representations for frequencies and frequency ratios.

class xenharmlib.core.frequencies.Frequency(number: int | Fraction | float | FrequencyRatio)

Frequency is the class to which all pitch definitions ultimately come down. Frequencies represent the physical layer of sound, stripped from all other abstractions.

Frequency is a wrapper around symbolic mathematical expressions that are provided by the sympy package. Using those expressions instead of floats allows us to do exact precision calculations. This is especially useful in regard to equal division tunings where pitches have irrational frequencies.

Frequency objects can be constructed by providing an integer, float, Fraction or a sympy expression. For example

>>> from xenharmlib import Frequency
>>> from fractions import Fraction
>>> Frequency(440)
Frequency(440)
>>> Frequency(1.5)
Frequency(3/2)
>>> Frequency(Fraction(3, 2))
Frequency(3/2)
>>> import sympy as sp
>>> Frequency(sp.Integer(2)**sp.Rational(1, 12))
Frequency(2**(1/12))

As you might have noticed floats get converted to fractions internally. This is done to ensure precision when dealing with frequencies, however despite this safeguard using floats has a lot of pitfalls, as you can see in this example:

>>> Frequency(0.2)
Frequency(3602879701896397/18014398509481984)

For very technical reasons floats are pretty bad at saving certain numbers. So even though it is possible to initialize a Frequency from a float it is highly discouraged. Instead, you should use python’s Fraction type

>>> Frequency(Fraction(2, 10))
Frequency(1/5)
>>> Frequency(440 + Fraction(2, 10))
Frequency(2201/5)

If you want a more human-readable form you can always convert to float after all calculations have been done:

>>> Frequency(440 + Fraction(2, 10)).to_float()
440.2

Frequency objects are part of a dimensionful arithmetic that is defined on Frequency objects and various scalars. Frequencies can interact with one another and with scalar values in the way how they would in a proper physical equation:

Frequencies can be added to and subtracted from other frequencies:

>>> Frequency(440) + Frequency(100)
Frequency(540)
>>> Frequency(440) - Frequency(100)
Frequency(340)

However, the same way as adding a dimensionless quality to a quantity in Hz is forbidden in a physical equation Frequency objects and scalar values can not be added in xenharmlib:

>>> Frequency(440) + 100
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'Frequency' and 'int'"

For multiplication the same holds in reverse. Frequencies can be multiplied by a scalar, but not with one other:

>>> from xenharmlib import FrequencyRatio
>>> 3 * Frequency(100)
Frequency(300)
>>> Frequency(200) * FrequencyRatio(3, 2)
Frequency(300)

A Frequency can be divided by both a scalar and a frequency. While the first case results in a Frequency in Hz, the second will be a scalar FrequencyRatio without a physical unit attached to it:

>>> Frequency(440) / 10
Frequency(44)
>>> Frequency(440) / Frequency(100)
FrequencyRatio(22/5)

Even though a frequency can be divided by a scalar, the same does not hold in reverse: Dividing a scalar by a frequency will raise an error. Even though an expression like 1 / 80 Hz is technically legal in a physics equation it is outside the scope of this implementation.

>>> 1 / Frequency(100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'int' and 'Frequency'"
get_harmonic(index: int) Frequency

Returns the k-th overtone frequency for this frequency.

Parameters:

index – Index of the harmonic. 0 is the original frequency, 1 the first harmonic, etc

get_harmonics(limit: Frequency | None = None) List[Frequency]

Returns a list of overtone frequencies for this note

Parameters:

limit – (optional) upper-frequency limit of the list in Hz, defaults to the average audible maximum of the human ear of 20KHz

to_float() float

Converts this object into a floating point number

to_int() int

Converts this object into an integer

class xenharmlib.core.frequencies.FrequencyRatio(numerator: int | Fraction | float | FrequencyRatio, denominator: int | Fraction | float | FrequencyRatio = 1)

The FrequencyRatio class can be understood as an augmented version of the Python built-in Fraction type. Different from the built-in Fraction type FrequencyRatio can also hold infinite-precision irrational fractions like \(\frac{\sqrt[12]{2}}{2^{\frac{1}{12}}}\) that are essential for equal temperament intervals.

For infinite-precision calculation, FrequencyRatio wraps the sympy package for symbolic arithmetic. Calculation results do not get converted to approximations until the user explicitly decides to.

FrequencyRatio objects can be created like Fractions:

>>> FrequencyRatio(20, 8)
FrequencyRatio(5/2)
>>> FrequencyRatio(3)
FrequencyRatio(3)

For both numerator and denominator, sympy expressions can be used:

>>> import sympy as sp
>>> FrequencyRatio(sp.Integer(3)**sp.Rational(3, 12), sp.sqrt(3))
FrequencyRatio(3**(3/4)/3)

Frequency ratios can also be constructed from prime exponent vectors (monzos):

>>> FrequencyRatio.from_monzo([-11, 7])
FrequencyRatio(2187/2048)

Floats are also supported, however highly discouraged because of the technical limitations of the data type that can lead to surprising and unwanted results:

>>> FrequencyRatio(0.2) # bad
FrequencyRatio(3602879701896397/18014398509481984)
>>> FrequencyRatio(2, 10) # good
FrequencyRatio(1/5)

Frequency ratios define a standard arithmetic and interact seamlessly with other scalar types:

>>> FrequencyRatio(20, 8) * FrequencyRatio(2)
FrequencyRatio(5)
>>> FrequencyRatio(20, 8) + 3
FrequencyRatio(11/2)
>>> 3 + FrequencyRatio(20, 8)
FrequencyRatio(11/2)
>>> from fractions import Fraction
>>> FrequencyRatio(20, 8) * Fraction(8, 20)
FrequencyRatio(1)
>>> Fraction(16, 20) * FrequencyRatio(20, 8)
FrequencyRatio(2)
>>> FrequencyRatio(20) / 10
FrequencyRatio(2)
>>> 5 / FrequencyRatio(20)
FrequencyRatio(1/4)
property cents

The cents equivalent of this frequency ratio

property denominator: FrequencyRatio

The denominator of the ratio

classmethod from_monzo(monzo: Tuple[int, ...])

Creates a frequency ratio from a monzo. A monzo is a list of exponents for the prime numbers, for example, the argument [-1, 1] creates the frequency \(2^{-1} * 3^1\)

log(base: int | Fraction | float | FrequencyRatio) FrequencyRatio

Returns the result of the logarithm of this object

Parameters:

base – The base that should be assumed for calculation

property numerator: FrequencyRatio

The numerator of the ratio

to_float() float

Converts this object into a floating point number

to_int() int

Converts this object into an integer

to_monzo()

Factorizes the frequency ratio into a monzo. A monzo is a list of exponents for the prime numbers, for example, the frequency ratio 3/2 creates the monzo [-1, 1], since \(2^{-1} * 3^1 = \frac{3}{2}\)

Lattices

The lattice module implements a generalization of pitch indices.

In sparse, equally-spaced tunings the pitch index group \((\mathbb{Z}, +)\) can be mapped to the frequency ratio group \((\mathbb{R}, \cdot)\) by group homomorphism \(H_b\) as follows:

\(H_b(x) := b^x\)

\(H_b(x + y) := b^x \cdot b^y = b^{(x + y)}\)

Lattices generalize this homomorphism by mapping a vector group \((\mathbb{Z}^n, +)\) to the frequency ratio group \((R, \cdot)\) refering to a base vector B:

\(H_B(X) := b_1^{x_1} \cdot b_2^{x_2} \cdot ... \cdot b_n^{x_n}\)

\(H_B(X + Y) := b_1^{(x_1 + y_1)} \cdot b_2^{(x_2 + y_2)} \cdot ... \cdot b_n^{(x_n + y_n)}\)

By defining an order on \(\mathbb{Z}^n\) with the following definition

\(X < Y\) iff \(H_B(X) < H_B(Y)\)

and by introducing scalar multiplication \(kX\) on \(\mathbb{Z}^n\) as a short form for repeated addition of an element \(X\) in \(\mathbb{Z}^n\) with itself, equivalency classes on \(\mathbb{Z}^n\) can be obtained by defining division with remainder:

\(\lfloor \frac{X}{Y} \rfloor = C\)

\(X \bmod Y = k\)

so that \(X = kY + C\) with \(C < Y\)

For \(n = 1\) this reduces to simple integer modulo arithmetic.

Using this definition for a n-dimensional pitch index both pitch class indices and base interval indices can be obtained with C being the n-dimensional pitch class index and k being the integer base interval index.

While for \(n = 1\) pitch class indices are finite, for \(n > 1\) pitch class indices are infinite.

class xenharmlib.core.lattice.Lattice(base: Tuple[FrequencyRatio, ...])

A Lattice is a point cloud representing a frequency ratio space. For a given base \(b_1, ...., b_n\) and integer lattice coordinates \(x_1, ..., x_n\) the point represents \(r(X) = b_1^{x_1} \cdot ... \cdot b_n^{x_n}\).

The 0 is understood as an n-dimensional 0-vector and can be obtained through the zero property.

Parameters:

base – A tuple of frequency ratios

contains_point(point: LatticePoint) bool

Returns True if a given lattice point is part of this lattice, False otherwise.

Parameters:

point – A lattice point

point(vector: Tuple[int, ...]) LatticePoint

Create a point inside this lattice

Parameters:

vector – The lattice integer coordinates

property zero: LatticePoint

Returns the lattice point with the zero vector coordinates

class xenharmlib.core.lattice.LatticePoint(vector: Tuple[int, ...], base: Tuple[FrequencyRatio, ...])

A point in a n-dimensional lattice representing a frequency ratio. For a given base \(b_1, ..., b_n\) and integer lattice coordinates \(x_1, ..., x_n\) the point represents \(r(X) = b_1^{x_1} \cdot ... \cdot b_n^{x_n}\).

The 0 is understood as an n-dimensional 0-vector and can be obtained through the zero() class method.

For two lattice points X and Y with the same base the class implements the following operations:

  • addition (X + Y): defined as vector addition, equivalent to frequency ratio multiplication r(X + Y) = r(X) * r(Y)

  • subtraction (X - Y): defined as vector subtraction, equivalent to frequency ratio division r(X - Y) = r(X) / r(Y)

  • scalar multiplication (k * X): defined as scalar vector multiplication

  • modulo (X % Y): being defined as X - kY so that (X - kY) < Y and (X - kY) >= 0

  • floordiv (X // Y): being defined for two lattice points X and Y as k with X - kY so that (X - kY) < Y and (X - kY) >= 0

  • equality (X == Y): defined as vector equality X == Y

  • ordering operations (X < Y): defined as X < Y iff r(X) < r(Y)

As unitary operators the class implements:

  • negation (-X): being defined as component-wise negation or, geometrically, point reflection through 0

  • abs (abs(X)): being defined as -X for X < 0, X otherwise

LatticePoints are also hashable, so they can be used in sets

Parameters:
  • vector – An integer tuple

  • base – A base vector of frequency ratios with the same dimensions as the vector

property frequency_ratio: FrequencyRatio

Returns the frequency ratio represented by this lattice point

classmethod zero(base: Tuple[FrequencyRatio, ...])

Returns the 0 vector for a given base

Origin Context

This module implements the OriginContext class which is the abstract base class for tunings and notations

class xenharmlib.core.origin_context.OriginContext(*, freq_repr_cls: type[FreqReprT], interval_cls: type[IntervalT], scale_cls: type[ScaleT], interval_seq_cls: type[IntervalSeqT], interval_fan_cls: type[IntervalFanT], freq_repr_seq_cls: type[FreqReprSeqT])

OriginContext is the abstract base class for both tunings and notations. It defines a unified interface for collection and interval builder methods as well as an interface for a defined “zero element” which is used as a reference point for other objects.

OriginContext does not define an interface for the builder method of the FreqRepr object (such as pitch or note) as the name and the function signature vary greatly from context to context (e.g. one dimensional tunings demand an integer as construction argument, more-dimensional tunings a vector, notes in periodic tunings mostly demand a string symbol and a base interval index, etc)

abstractmethod closest_freq_repr(frequency: Frequency) FreqReprT

Returns the frequency representation closest to a given frequency. This function is only implemented for origin contexts which mathematically allow to determine a closest frequency representation.

If the origin context e.g. is a prime limit tuning every frequency can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

abstractmethod closest_interval(frequency_ratio: FrequencyRatio) IntervalT

Returns the interval closest to a given frequency ratio. This function is only implemented for origin contexts which mathematically allow to determine a closest frequency ratio approximation.

If the origin context e.g. is a prime limit tuning every frequency ratio can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

abstractmethod closest_interval_fan(frequency_ratios: Iterable[FrequencyRatio]) IntervalFanT

Returns the interval fan closest to a given iterable of frequency ratios. This function is only implemented for origin contexts which mathematically allow to determine a closest frequency ratio approximation.

If the origin context e.g. is a prime limit tuning every frequency ratio can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

abstractmethod closest_interval_seq(frequency_ratios: Iterable[FrequencyRatio]) IntervalSeqT

Returns the interval sequence closest to a given iterable of frequency ratios. This function is only implemented for origin contexts which mathematically allow to determine a closest frequency ratio approximation.

If the origin context e.g. is a prime limit tuning every frequency ratio can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

abstractmethod closest_scale(frequencies: Iterable[Frequency]) ScaleT

Returns the scale closest to a given iterable of frequencies This function is only implemented for origin contexts which mathematically allow to determine a closest frequency representation.

If the origin context e.g. is a prime limit tuning every frequency can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

abstractmethod closest_seq(frequencies: Iterable[Frequency]) FreqReprSeqT

Returns the sequence closest to a given iterable of frequencies This function is only implemented for origin contexts which mathematically allow to determine a closest frequency representation.

If the origin context e.g. is a prime limit tuning every frequency can be approximated infinitesimally close. In this case the method should raise a TypeError

(Must be implemented by subclasses)

diff_interval(pitch_diff: IndexT) IntervalT

Returns an interval the size of a given pitch index difference.

Parameters:

pitch_diff – The pitch index difference

diff_interval_fan(pitch_diffs: Iterable[IndexT] | None = None) IntervalFanT

Returns an interval fan from an iterable of pitch index differences, for example:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> major_chord = edo12.diff_interval_fan([4, 7])
>>> minor_chord = edo12.diff_interval_fan([3, 7])
Parameters:

pitch_diffs – An iterable containing pitch index differences

diff_interval_seq(pitch_diffs: Iterable[IndexT] | None = None) IntervalSeqT

Returns an interval sequence from an iterable of pitch index differences, for example:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> major_chord = edo12.diff_interval_seq([4, 3])
>>> minor_chord = edo12.diff_interval_seq([3, 4])
Parameters:

pitch_diffs – An iterable containing pitch index differences

interval(source: FreqReprT, target: FreqReprT) IntervalT

Returns an interval having the interval type this origin context was configured with

Raises:

IncompatibleOriginContexts – If either source or target have a different origin context than this one

Parameters:
  • source – The source of the interval

  • target – The target of the interval

interval_fan(intervals: Iterable[IntervalT] | None = None) IntervalFanT

Returns an interval fan having the interval fan type this origin context was configured with

Raises:

IncompatibleOriginContexts – If at least one given element has a different origin context than this one

Parameters:

intervals – A list of intervals originating from this context

interval_seq(intervals: Iterable[IntervalT] | None = None) IntervalSeqT

Returns an interval sequence having the interval sequence type this origin context was configured with

Raises:

IncompatibleOriginContexts – If at least one given element has a different origin context than this one

Parameters:

intervals – A list of intervals originating from this context

scale(elements: Iterable[FreqReprT] | None = None) ScaleT

Returns a scale having the scale type this origin context was configured with

Raises:

IncompatibleOriginContexts – If at least one given element has a different origin context than this one

Parameters:

elements – A list of frequency representations originating from this context

seq(elements: Iterable[FreqReprT] | None = None) FreqReprSeqT

Returns a pitch/note sequence

Raises:

IncompatibleOriginContexts – If at least one given element has a different origin context than this one

Parameters:

elements – A list of elements originating from this context

property unison_diff: IndexT

The unison diff is the pitch difference of the unison interval. In tunings with integer indexing this is 0, in tunings with lattice indexing this is typically the zero-vector.

property unison_interval: IntervalT

The unison interval is a reference point, in one-dimensional tunings it is the element with pitch diff 0, in western notation typically P1, etc.

abstract property zero_element: FreqReprT

The zero element is a reference point, in one-dimensional tunings it is the element with index 0, in western notation typically C-0, etc

abstract property zero_index: IndexT

The zero index is a reference point, in tunings with integer indexing this is 0, in tunings with lattice indexing this is typically the zero-vector.

Frequency Representations

This module implements base classes for frequency representations (pitches, notes, etc)

class xenharmlib.core.freq_repr.FreqRepr(origin_context, frequency: Frequency)

FreqRepr is the base class for all frequency representations (pitches, notes, etc). The class implements a total order on the set of all frequency representations (based on the total order of frequency values). It also saves the origin context of the frequency representation (a tuning or notation)

In addition it defines an abstract method for short string representation and a proxy method ‘interval’ which redirects to the interval method of the origin context.

property frequency: Frequency

The frequency of this object

interval(other: Self)

Returns an interval between this frequency representation and another representation of the same origin context.

Parameters:

other – Another frequency representation of the same origin context

property origin_context

The origin context associated with this frequency representation

reflection(axis: Self | None = None) Self

A reflection of this pitch/note across a pitch/note axis. In technical terms the method calculates the interval from this object to the axis and then applies that interval to to the axis.

Parameters:

axis – The element across which this object is reflected (optional, defaults to the zero element of the origin context)

retune_closest(origin_context) Self

Gets the frequency representation in a target origin context that is closest to the frequency of this object.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency

scale(istruct)

Depending on given parameter this method constructs a new scale with this pitch/note as a starting point (if an interval sequence is given), or as a reference point (if an interval fan is given)

Parameters:

istruct – An interval sequence or interval fan of the same origin context

seq(istruct)

Depending on given parameter this method constructs a new sequence with this pitch/note as a starting point (if an interval sequence is given), or as a reference point (if an interval fan is given)

Parameters:

istruct – An interval sequence or interval fan of the same origin context

abstract property short_repr: str

(Must be implemented by subclasses) A shortened representation of this note (to be used in collection objects like scales)

abstractmethod transpose(diff) Self

(Must be implemented by subclasses) Transposes the frequency representation

class xenharmlib.core.freq_repr.IndexedFreqRepr(origin_context, frequency: Frequency, pitch_index: IndexT)

Base class for indexed frequency representation objects. Assumes a pitch index in addition to the frequency as part of the data structure.

Implements optimizations on the total order based on pitch index data (if objects originate from same context).

Demands that subclasses implement a transpose method

property pitch_index: IndexT

The pitch index of this object

Scales

This module implements base classes for scales

class xenharmlib.core.scale.PeriodicScale(origin_context, elements: Iterable[FreqReprT] | None = None)

PeriodicScale is the abstract base class for scales that contain frequency representations of periodic tunings / notations. It overwrites the set operations to allow for the ignore_bi_index flag.

It implements the following additional methods:
  • pcs_normalized

  • pcs_intersection

  • is_equivalent

  • rotated_up

  • rotated_down

  • rotation

Subclasses must implement at least the transpose function

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • elements – A list of frequency representations

difference(other: Self, ignore_bi_index: bool = False) Self

Returns a scale containing only elements from this scale that are NOT present in the other scale

Parameters:
  • other – Another scale of the same origin context

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same. For example, if the difference of two scales including C-0 and C-1 respectively is calculated, C-0 will not be inserted into the new scale

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

intersection(other: Self, ignore_bi_index: bool = False) Self

Returns a new scale including all elements that are included in both scales.

Parameters:
  • other – Another scale of the same origin context

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same. For example, if the intersection of two scales including C-0 and C-1 respectively is calculated, both elements will be added to the result

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

is_disjoint(other: Self, ignore_bi_index: bool = False) bool

Determines if this scale has any common elements with another scale of the same origin context

Parameters:
  • other – Another scale of the same origin context

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same. For example, if one scale includes C-0 and another C-1 the scales will not be considered disjoint

Raises:

IncompatibleOriginContexts – If the other scale originates from a different origin context

is_equivalent(other: PeriodicScale) bool

Deprecated since version 0.3.0: Use is_set_equivalent() instead.

Returns True if two scales are set equivalent, i.e. every element in this scale has an equivalent element somewhere(!) in the other scale (and vice versa).

Periodic scales of different origin contexts can be compared if their origin contexts have the same equivalency interval. Set equivalency between scales of different contexts is defined as “equality after pitch class set normalization”

Raises:

IncompatibleOriginContexts – If the other scale has a different equivalency interval definition

Parameters:

other – Another periodic scale

property is_pcs_normalized: bool

Returns bool if this scale is pcs normalized. A pcs normalized scale only contains elements with base interval index 0.

property is_period_normalized: bool

Returns bool if this scale is period normalized. A period normalized scale only contains elements smaller than the equivalent of the root note, meaning if the scale starts with F3, all subsequent notes are smaller than F4.

property is_plusone_normalized: bool

Returns a period normalized version of the scale with an additional transposed root at the end, e.g. (F0, G0, A0, B0, C1, D1, E1, F1)

is_seq_equivalent(other: PeriodicScale) bool

Returns True if two scales are sequentially equivalent, i.e. every element in this scale corresponds to another one in the other scale at the same scale index.

Periodic scales of different origin contexts can be compared if their origin contexts have the same equivalency interval. Sequential equivalency between scales of different contexts is defined as “equality after base interval alignment”

Raises:

IncompatibleOriginContexts – If the other scale has a different equivalency interval definition

Parameters:

other – Another periodic scale

is_set_equivalent(other: PeriodicScale) bool

Returns True if two scales are set equivalent, i.e. every element in this scale has an equivalent element somewhere(!) in the other scale (and vice versa).

Periodic scales of different origin contexts can be compared if their origin contexts have the same equivalency interval. Set equivalency between scales of different contexts is defined as “equality after pitch class set normalization”

Raises:

IncompatibleOriginContexts – If the other scale has a different equivalency interval definition

Parameters:

other – Another periodic scale

is_subset(other: Self, proper: bool = False, ignore_bi_index: bool = False) bool

Determines if all elements in this scale also exist in the other scale.

Parameters:
  • other – Another scale of the same origin context

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same.

Raises:

IncompatibleOriginContexts – If the other scale originates from a different origin context

is_superset(other: Self, proper: bool = False, ignore_bi_index: bool = False) bool

Determines if all elements in the other scale also exist in this scale.

Parameters:
  • other – Another scale of the same origin context

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same.

Raises:

IncompatibleOriginContexts – If the other scale originates from a different origin context

property is_zp_normalized: bool

Returns bool if the first element is the zero element of the origin context (pitch 0 in tunings, in western-like notations typically C0) and all elements reside in the first base interval.

pcs_intersection(other: Self) Self

Returns a scale including all elements whose pitch class resides in both of the scales, normalized to the first base interval.

Parameters:

other – The other scale

pcs_normalized() Self

Returns a normalized version of this scale where all the elements of the scale are put into the first base interval of the tuning

Note: If the original scale has equivalent element pairs the normalized scale will be smaller in cardinality.

period_normalized() Self

Returns a version of the scale in which each element that is above the root element will be transposed to the interval between the root element and its equivalent in the next base interval. For example the scale of the Fm7/11 chord with notes (F0, Ab0, C1, Eb1, Bb1) will become the scale (F0, Ab0, Bb0, C1, Eb1)

plusone_normalized() Self

Returns a period normalized version of the scale with an additional transposed root at the end, e.g. (F0, G0, A0, B0, C1, D1, E1, F1)

rotated_down() Self

Create a new scale by transposing the base interval of the highest element downwards until it is below the lowest element

rotated_up() Self

Create a new scale by transposing the base interval of the lowest element upwards until it is above the highest element

rotation(order: int) Self

Returns the n-th rotation of this scale.

Parameters:

order – The number of times the scale is rotated. If a negative number is given the scale will be rotated downwards. On 0 the scale will return itself

symmetric_difference(other: Self, ignore_bi_index: bool = False) Self

Returns a scale that includes all the elements from both scales that exist in either of them but NOT BOTH. This is the complement operation of the intersection.

Parameters:
  • other – Another scale of the same origin context

  • ignore_bi_index – (Optional, default False) When set to True elements of the same pitch class will be treated the same. For example, if the difference of two scales including C-0 and C-1 respectively is calculated, C-0 will not be inserted into the new scale

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

transpose_bi_index(bi_diff: int) Self

Returns a scale with the same pitch class indices and symbols, but with a transposed base interval

Parameters:

bi_diff – The difference in base interval between this scale and the resulting one

zp_normalized() Self

Returns the scale transposed in a way so the root has pitch index 0 and all elements reside in the first base interval. In notations with enharmonic ambiguity a designated zero note is used (in western-like notations typically C0)

The function is equivalent to successively invoking zero_normalized + period_normalized or (which is the same) zero_normalized + pcs_normalized

class xenharmlib.core.scale.Scale(origin_context, elements: Iterable[FreqReprT] | None = None)

Scale is the abstract base class for all scale types. The class implements iteration, the ‘in’ operator, the == operator, item retrieval with [] and set operations.

Subclasses must implement at least the transpose function

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • elements – A list of frequency representations

difference(other: Self) Self

Returns a scale containing only elements from this scale that are NOT present in the other scale

Parameters:

other – Another scale of the same origin context

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

property frequencies

An ordered list of frequencies present in this scale

intersection(other: Self) Self

Returns a new scale including all elements that are included in both scales.

Parameters:

other – Another scale of the same origin context

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

is_disjoint(other: Self) bool

Determines if this scale has any common elements with another scale of the same origin context.

Parameters:

other – Another scale of the same origin context

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

is_subset(other: Self, proper: bool = False) bool

Determines if all elements in this scale also exist in the other scale.

Parameters:
  • other – Another scale of the same origin context

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

is_superset(other: Self, proper: bool = False) bool

Determines if all elements in the other scale also exist in this scale.

Parameters:
  • other – Another scale of the same origin context

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

abstract property is_zero_normalized: bool

Returns True if this function is zero normalized, meaning that the first element of the scale is identical to the zero element of the origin context (pitch 0 in tunings, typically C0 in western-like notations)

(must be implemented by subclass, since comparison to the the zero note should be done according to notational identity)

property origin_context

The origin context from which this scale was built

partial(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new scale consisting of a selection of indices of this scale. The selection is defined by an index mask expression.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a scale including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the scale, for example (2, …) will select all elements of the scale except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this scale.

partial_not(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new scale consisting of a selection of indices of this scale. The selection will be determined by an index mask and will hold all elements whose index is NOT covered by the mask.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a scale including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the scale, for example (2, …) will select all elements of the scale except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this scale.

partition(mask_expr: int | Tuple[int | EllipsisType, ...]) Tuple[Self, Self]

Partitions the scale into two parts using an index mask. The function will return a tuple of two scales with the first scale including all indices that are covered by the index mask and the second one including all indices that are not.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a scale including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the scale, for example (2, …) will select all elements of the scale except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this scale.

reflection(axis: FreqReprT | None = None) Self

A reflection of each pitch/note across a pitch/note axis. In technical terms the method calculates the interval from each element to the axis and then applies that interval to to the axis.

Parameters:

axis – The element across which this object is reflected (optional, defaults to the zero element of the origin context)

If scale is reflected across the zero element, reflection is equivalent to inversion of the pitch class set

retune_closest(origin_context) Self

Gets the scale in a target origin context that is closest to the frequency series of this scale.

A caveat: Since scales are a structure of sorted unique frequency representation this method may produce a scale with a smaller size than the original because two representations in this context can be approximated to the same representation in the target context.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency

spec_interval(source_index, target_index)

Returns the specific interval for a generic interval. For example in the 12-EDO C major scale the generic interval defined by (0, 2) is the specific interval major 3.

Parameters:
  • source_index – Source index for the interval

  • target_index – Target index for the interval

symmetric_difference(other: Self) Self

Returns a scale that includes all the elements from both scales that exist in either of them but NOT BOTH. This is the complement operation of the intersection.

Parameters:

other – Another scale of the same origin context

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

to_interval_fan(ref: Self | None = None)

Returns this scale represented as an interval fan

to_interval_seq()

Returns this scale represented as an interval sequence

to_intervals() List[Interval[FreqReprT]]

Deprecated since version 0.3.0: Use to_interval_seq() instead.

Returns this scale represented as a list of intervals

to_seq()

Returns this scale represented as sequence of frequency representations

abstractmethod transpose(diff) Self

Transposes the scale by the given difference (must be overwritten by subclass)

Parameters:

diff – An interval or interval-like object

union(other: Self) Self

Returns a new scale including all elements from this scale as well as the other

Parameters:

other – Another scale of the same origin context

Raises:

IncompatibleOriginContexts – If the other scale has a different origin context

with_element(element: FreqReprT) Self

Returns a new scale containing all elements from this scale and the additional one given as a parameter.

Parameters:

element – The new element to be added to the result

Raises:

IncompatibleOriginContexts – If element has a different origin context than this scale

zero_normalized() Self

Returns the scale transposed in a way so the root has pitch index 0. In notations with enharmonic ambiguity a designated zero note is used (in western-like notations typically C0)

Intervals

This module implements base classes for intervals

class xenharmlib.core.interval.IndexedInterval(origin_context, frequency_ratio: FrequencyRatio, pitch_diff: IndexT)

IndexedInterval extends the Interval class by a pitch_diff property.

class xenharmlib.core.interval.Interval(origin_context, frequency_ratio: FrequencyRatio)

Interval is the abstract bass class for all interval types, consisting only of an origin context and a frequency ratio. Based on frequency ratio it implements the cents property and a total ordering.

It forces subclasses to implement the __abs__ method, the class method from_source_and_target and the property short_repr

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • frequency_ratio – A frequency ratio object

property cents: float

The interval size in cents (e.g. 1200 for an octave)

property frequency_ratio: FrequencyRatio

The frequency ratio of this interval (e.g. 2 for an octave)

abstractmethod classmethod from_source_and_target(source: FreqReprT, target: FreqReprT) Self

Constructs an interval from two frequency representations (must be implemented by subclass)

Parameters:
  • source – The starting point of the interval

  • target – The end point of the interval

property origin_context

The context this interval originated from (like a tuning or notation)

retune_closest(origin_context) Self

Gets the interval in a target origin context that is closest to the frequency ratio of this object.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency ratio

abstract property short_repr: str

A short string representation of the interval (must be implemented by subclass)

property sign: int

Returns 1 if this interval is an upward interval, -1 if it is a downward interval and 0 if it is the unison interval

Interval Sequences

class xenharmlib.core.interval_seq.IntervalSeq(origin_context, intervals: Iterable[IntervalT] | None = None)

IntervalSeq is the abstract base class for all interval sequence types. Interval sequences can be understood as “abstract scales” (for example the minor scale as such, instead of C minor). Interval sequences have multiple applications from templating to structure discovery.

In line with its Sequence superclass interval sequences implement iteration, the ‘in’ operator, the == operator, item retrieval with [], concatenation with +, repeated self-concatenation with *, searching with index, and len().

Like scale types interval sequences also allow partitioning with partial, partial_not and partition.

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • intervals – A sequence of intervals from the origin context

property cents: List[float]

An ordered list of cent values representing the sequence

property frequency_ratios

An ordered list of frequency ratios present in this sequence

index(interval, start=0, end=None, /) int

Return first index of interval (similar to the index method of python’s list)

Parameters:
  • interval – The interval to search for

  • start – If set method ignores occurences before given index (optional, defaults to 0)

  • end – If set method ignores occurences after given index (optional, defaults to end of sequence)

Raises:

ValueError – If interval was not found in sequence

inversion() Self

Returns an interval sequence with all ascending intervals flipped into descending intervals and vice versa.

Warning

Inversion has a different meaning on intervals and interval sequences. This function does not call the inversion function on every interval in the sequence but the negation (-) function

property origin_context

The origin context from which this interval sequence was built

partial(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new sequence consisting of a selection of indices of this sequence. The selection is defined by an index mask expression.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a sequence including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all intervals of the sequence except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

partial_not(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new sequence consisting of a selection of indices of this sequence. The selection will be determined by an index mask and will hold all intervals whose index is NOT covered by the mask.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a sequence including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all intervals of the sequence except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

partition(mask_expr: int | Tuple[int | EllipsisType, ...]) Tuple[Self, Self]

Partitions the sequence into two parts using an index mask. The function will return a tuple of two sequences with the first sequence including all indices that are covered by the index mask and the second one including all indices that are not.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) defines a sequence including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all intervals of the sequence except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

property pitch_diffs: List[IndexT]

An ordered list of pitch differences representing the sequence

retune_closest(origin_context) Self

Gets the interval sequence in a target origin context that is closest to the frequency ratio series of this sequence.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency ratio

to_scale(start: FreqRepr) Scale

Returns a scale that has the interval structure of this sequence, starting with the given note/pitch

Parameters:

start – A starting note/pitch of the same origin context

to_seq(start: FreqRepr) Scale

Returns a pitch/note sequence that has the interval structure of this object, starting with the given note/pitch

Parameters:

start – A starting note/pitch of the same origin context

with_interval(interval: IntervalT, insert_pos: int | None = None) Self

Returns a new interval sequence containing all intervals from this sequence and the additional one given as a parameter. By default new intervals appear at the end of the sequence.

Parameters:
  • interval – The new interval to be added to the result

  • insert_pos – The insertion position of the new interval. 0 will insert the interval at the front, 1 will insert the interval at the second position, etc. (optional, default is None which results in the value len(sequence) + 1)

Raises:

IncompatibleOriginContexts – If interval has a different origin context than this sequence

Interval Fans

class xenharmlib.core.interval_fan.IntervalFan(origin_context, intervals: Iterable[IntervalT] | None = None)

IntervalSeq is the abstract base class for all interval fan types. An interval fan is a list of intervals that all refer to a single reference pitch (e.g. in prime limit tunings (1/1, 5/4, 3/2) is the interval fan for a pure major triad, all referencing to a common point of origin)

In line with its Sequence superclass interval fans implement iteration, the ‘in’ operator, the == operator, item retrieval with [], concatenation with +, repeated self-concatenation with *, searching with index, and len().

Like scale types interval fans also allow partitioning with partial, partial_not and partition.

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • intervals – A sequence of intervals from the origin context

property cents: List[float]

An ordered list of cent values representing the fan

property frequency_ratios

An ordered list of frequency ratios present in this fan

index(interval, start=0, end=None, /) int

Return first index of interval (similar to the index method of python’s list)

Parameters:
  • interval – The interval to search for

  • start – If set method ignores occurences before given index (optional, defaults to 0)

  • end – If set method ignores occurences after given index (optional, defaults to end of fan)

Raises:

ValueError – If interval was not found in fan

inversion() Self

Returns an interval fan with all ascending intervals flipped into descending intervals and vice versa.

Warning

Inversion has a different meaning on intervals and interval fans. This function does not call the inversion function on every interval in the fan but the negation (-) function

property origin_context

The origin context from which this interval fan was built

partial(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new fan consisting of a selection of indices of this fan. The selection is defined by an index mask expression.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a fan including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the fan, for example (2, …) will select all intervals of the fan except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this fan.

partial_not(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new fan consisting of a selection of indices of this fan. The selection will be determined by an index mask and will hold all intervals whose index is NOT covered by the mask.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a fan including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the fan, for example (2, …) will select all intervals of the fan except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this fan.

partition(mask_expr: int | Tuple[int | EllipsisType, ...]) Tuple[Self, Self]

Partitions the fan into two parts using an index mask. The function will return a tuple of two fans with the first fan including all indices that are covered by the index mask and the second one including all indices that are not.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) defines a fan including the second, third and sixth interval of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the fan, for example (2, …) will select all intervals of the fan except the first two.

If only one interval should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this fan.

property pitch_diffs: List[IndexT]

An ordered list of pitch differences representing the fan

retune_closest(origin_context) Self

Gets the interval fan in a target origin context that is closest to the frequency ratio series of this sequence.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency ratio

to_scale(reference: FreqRepr) Scale

Returns a scale that has the interval structure of this fan, with the given note/pitch being the pitch mapped to the unison

Parameters:

reference – A reference note/pitch of the same origin context

to_seq(reference: FreqRepr) Scale

Returns a pitch/note fan that has the interval structure of this fan, with the given note/pitch being the pitch mapped to the unison

Parameters:

reference – A reference note/pitch of the same origin context

with_interval(interval: IntervalT, insert_pos: int | None = None) Self

Returns a new interval fan containing all intervals from this fan and the additional one given as a parameter. By default new intervals appear at the end of the fan.

Parameters:
  • interval – The new interval to be added to the result

  • insert_pos – The insertion position of the new interval. 0 will insert the interval at the front, 1 will insert the interval at the second position, etc. (optional, default is None which results in the value len(fan) + 1)

Raises:

IncompatibleOriginContexts – If interval has a different origin context than this fan

Pitch / Note Sequences

class xenharmlib.core.freq_repr_seq.FreqReprSeq(origin_context, elements: Iterable[FreqReprT] | None = None)

FreqReprSeq is the abstract base class for all pitch/note sequence types.

In line with its Sequence superclass frequency representation sequences implement iteration, the ‘in’ operator, the == operator, item retrieval with [], concatenation with +, repeated self-concatenation with *, searching with index, and len().

Like scale types sequences also allow partitioning with partial, partial_not and partition.

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • elements – A sequence of elements from the origin context

property frequencies

An ordered list of frequencies present in this sequence

index(element, start=0, end=None, /) int

Return first index of element (similar to the index method of python’s list)

Parameters:
  • element – The element to search for

  • start – If set method ignores occurences before given index (optional, defaults to 0)

  • end – If set method ignores occurences after given index (optional, defaults to end of sequence)

Raises:

ValueError – If element was not found in sequence

inversion() Self

Returns the inversion of this sequence, which is defined as transforming all ascending intervals in the sequence into their descending counterpart and vice versa.

is_subseq(seq: Self, proper=False)

Returns True if this sequence is a subsequence of another one, False otherwise.

Parameters:
  • seq – The (possible) supersequence

  • proper – (optional, default is False). If set to True function will return False if sequences are identical

is_superseq(seq: Self, proper=False)

Returns True if this sequence is a supersequence of another one, False otherwise.

Parameters:
  • seq – The (possible) subsequence

  • proper – (optional, default is False). If set to True function will return False if sequences are identical

abstract property is_zero_normalized: bool

Returns True if this function is zero normalized, meaning that the first element of the scale is identical to the zero element of the origin context (pitch 0 in tunings, typically C0 in western-like notations)

(must be implemented by subclass, since comparison to the the zero note should be done according to notational identity)

property origin_context

The origin context from which this sequence was built

partial(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new sequence consisting of a selection of indices of this sequence. The selection is defined by an index mask expression.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a sequence including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all elements of the sequence except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

partial_not(mask_expr: int | Tuple[int | EllipsisType, ...]) Self

Returns a new sequence consisting of a selection of indices of this sequence. The selection will be determined by an index mask and will hold all elements whose index is NOT covered by the mask.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) gives a sequence including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all elements of the sequence except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

partition(mask_expr: int | Tuple[int | EllipsisType, ...]) Tuple[Self, Self]

Partitions the sequence into two parts using an index mask. The function will return a tuple of two sequences with the first sequence including all indices that are covered by the index mask and the second one including all indices that are not.

An index mask can be defined as a tuple of consecutive indices, e.g. (1, 2, 5) defines a sequence including the second, third and sixth element of this one.

An ellipsis between two indices indicates that all indices between them should be selected as well, e.g. (1, …, 5, 9) is equivalent to (1, 2, 3, 4, 5, 9).

If a mask begins with an ellipsis all indices from 0 to the next index are added to the selection, e.g. (…, 3, 5) is equivalent to (0, 1, 2, 3, 5).

A mask without a last index is called right-open and will select all indices from the last index to the end of the sequence, for example (2, …) will select all elements of the sequence except the first two.

If only one element should be selected a simple integer can be used

Parameters:

mask_expr – An index mask expression which defines the selection of indices from this sequence.

retrograde() Self

Returns the retrograde of this sequence, which is defined as “mirroring” the sequence so the last note becomes the first note and vice versa

retune_closest(origin_context) Self

Gets the sequence in a target origin context that is closest to the frequency series of this sequence.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target context does not have a proper definition of a closest representation to a given frequency

subseq_index(subseq: Self) int

Given a (possible) subsequence this method returns the starting index of the subsequence in this sequence or raises ValueError (if given parameter turned out not to be a subsequence)

This method works across origin contexts

Parameters:

subseq – The subsequence to search for

Raises:

ValueError – If subsequence was not found or an empty sequence was given as parameter

to_interval_fan(ref: Self | None = None)

Returns this sequence represented as an interval fan

to_interval_seq()

Returns this sequence represented as an interval sequence

transpose(diff) Self

Transposes the sequence by the given difference

with_element(element: FreqReprT, insert_pos: int | None = None) Self

Returns a new sequence containing all elements from this sequence and the additional one given as a parameter. By default new elements appear at the end of the sequence.

Parameters:
  • element – The new element to be added to the result

  • insert_pos – The insertion position of the new element. 0 will insert the element at the front, 1 will insert the element at the second position, etc. (optional, default is None which results in the value len(sequence) + 1)

Raises:

IncompatibleOriginContexts – If element has a different origin context than this sequence

zero_normalized() Self

Returns the sequence transposed in a way so the first element has pitch index 0. In notations with enharmonic ambiguity a designated zero note is used (in western-like notations typically C0)

class xenharmlib.core.freq_repr_seq.PeriodicFreqReprSeq(origin_context, elements: Iterable[FreqReprT] | None = None)

PeriodicFreqReprSeq is the abstract base class for frequency representation sequences that contain frequency representations of periodic tunings / notations.

It implements the following additional methods:
  • transpose_bi_index

  • is_equivalent

Parameters:
  • origin_context – An origin context (like a tuning or a notation)

  • elements – A list of frequency representations

is_equivalent(other: PeriodicFreqReprSeq) bool

Returns True if two sequences are equivalent, i.e. every element in this sequence corresponds to another one in the other sequence at the same index.

Periodic sequences of different origin contexts can be compared if their origin contexts have the same equivalency interval. Equivalency between sequences of different contexts is defined as “equality after base interval alignment”

Raises:

IncompatibleOriginContexts – If the other sequence has a different equivalency interval definition

Parameters:

other – Another periodic scale

transpose_bi_index(bi_diff: int) Self

Returns a sequence with the same pitch class indices and symbols, but with a transposed base interval

Parameters:

bi_diff – The difference in base interval between this sequence and the resulting one

Tuning

A tuning is the middle piece between the continuous world of frequencies and the discrete world of pitch.

In this module, you will find a collection of tuning classes, each with a certain set of assumptions built into them. Some tuning classes can be used as they are to create tuning objects, some are abstract classes that need a couple of methods implemented by a subclass.

class xenharmlib.core.tunings.EDOTuning(divisions, *, pitch_cls: type[EDOPitch] = <class 'xenharmlib.core.pitch.EDOPitch'>, pitch_interval_cls: type[EDOPitchInterval] = <class 'xenharmlib.core.pitch.EDOPitchInterval'>, pitch_scale_cls: type[EDOPitchScale] = <class 'xenharmlib.core.pitch_scale.EDOPitchScale'>, pitch_interval_seq_cls: type[EDOPitchIntervalSeq] = <class 'xenharmlib.core.pitch_interval_seq.EDOPitchIntervalSeq'>, pitch_interval_fan_cls: type[EDOPitchIntervalFan] = <class 'xenharmlib.core.pitch_interval_fan.EDOPitchIntervalFan'>, pitch_seq_cls: type[EDOPitchSeq] = <class 'xenharmlib.core.pitch_seq.EDOPitchSeq'>, ref_frequency: Frequency = Frequency(<Mock name='mock.Rational()' id='130468837690256'>))

EDOTuning (“equal division of the octave tuning”) divides an octave into pitches equally spaced from each other.

Parameters:

divisions – The number of divisions of the base interval

Optional keyword-only arguments

Parameters:

ref_frequency – A reference frequency on which this tuning is built. For EDOTunings this is the lowest pitch (pitch index 0). Defaults to the frequency for C0 in EDO tunings for A4 = 440Hz (about 16.35 Hz)

The following optional keyword-only arguments are only relevant if you are building your own tuning class:

Parameters:
  • pitch_cls – The python class that is used for the pitch object returned from the pitch method.

  • pitch_interval_cls – The python class that is used for the pitch interval object returned from the interval builder methods.

  • pitch_scale_cls – The python class that is used for the pitch scale object returned from the scale builder methods.

  • pitch_interval_seq_cls – The python class that is used for the pitch interval sequence object returned from the interval sequence builder methods.

  • pitch_interval_fan_cls – The python class that is used for the pitch interval fan object returned from the interval fan builder methods.

  • pitch_seq_cls – The python class that is used for the pitch sequence object returned from the sequence builder methods.

property best_fifth

Returns the pitch that best approximates the pure fifth (frequency ratio 3/2) in this tuning.

property fifth

Returns the pitch that represents the fifth of this tuning. In the default implementation, this is the best fifth, however, subclasses can also overwrite this behavior, so e.g. the second-best fifth is returned.

get_ring_number(pitch: EDOPitch | None = None) int

Returns the greatest common divisor of a pitch and the period length of the tuning.

Parameters:

pitch – A pitch of this tuning. (Optional, defaults to the pitch that best approximates the perfect fifth)

property name: str

The name of this tuning

property sharpness: int

Sharpness is an indicator of the pitch difference in EDO tunings between a natural and their sharp version (for example the steps needed to reach C# from C)

The sharpness of an EDO is defined by 7 times the pitch difference between the base pitch and the perfect fifth approximation minus 4 times the pitch difference in an octave.

class xenharmlib.core.tunings.EDTuning(divisions, eq_ratio: FrequencyRatio, *, pitch_cls: type[EDPitch] = <class 'xenharmlib.core.pitch.EDPitch'>, pitch_interval_cls: type[EDPitchInterval] = <class 'xenharmlib.core.pitch.EDPitchInterval'>, pitch_scale_cls: type[EDPitchScale] = <class 'xenharmlib.core.pitch_scale.EDPitchScale'>, pitch_interval_seq_cls: type[EDPitchIntervalSeq] = <class 'xenharmlib.core.pitch_interval_seq.EDPitchIntervalSeq'>, pitch_interval_fan_cls: type[EDPitchIntervalFan] = <class 'xenharmlib.core.pitch_interval_fan.EDPitchIntervalFan'>, pitch_seq_cls: type[EDPitchSeq] = <class 'xenharmlib.core.pitch_seq.EDPitchSeq'>, ref_frequency: Frequency = Frequency(<Mock name='mock.Rational()' id='130468837690256'>))

EDTuning (“equal division tuning”) takes a base interval given as a frequency ratio and divides this base interval into pitches equally spaced from one another.

For example, the Bohlen-Pierce tuning can be created like this:

>>> from xenharmlib import EDTuning
>>> from xenharmlib import FrequencyRatio
>>> BP = EDTuning(
...     divisions=13,
...     eq_ratio=FrequencyRatio(3)
... )
Parameters:
  • divisions – The number of divisions of the base interval

  • eq_ratio – The frequency factor defining the base interval (e.g. 2 for an octave, 3/2 for a fifth)

Optional keyword-only arguments

Parameters:

ref_frequency – A reference frequency on which this tuning is built. For EDTunings this is the lowest pitch (pitch index 0). Defaults to the frequency for C0 in EDO tunings for A4 = 440Hz (about 16.35 Hz)

The following optional keyword-only arguments are only relevant if you are building your own tuning class:

Parameters:
  • pitch_cls – The python class that is used for the pitch object returned from the pitch method.

  • pitch_interval_cls – The python class that is used for the pitch interval object returned from the interval builder methods.

  • pitch_scale_cls – The python class that is used for the pitch scale object returned from the scale builder methods.

  • pitch_interval_seq_cls – The python class that is used for the pitch interval sequence object returned from the interval sequence builder methods.

  • pitch_interval_fan_cls – The python class that is used for the pitch interval fan object returned from the interval fan builder methods.

  • pitch_seq_cls – The python class that is used for the pitch sequence object returned from the sequence builder methods.

get_frequency(pitch: EDPitch) Frequency

Deprecated since version 0.3.0: Use the frequency property of the pitch object instead

Returns the frequency of a given note

Parameters:

note – A note from this tuning

Raises:

IncompatibleOriginContexts – If note is from a different tuning

get_frequency_for_index(pitch_index: int) Frequency

Returns the frequency for a given pitch index

Parameters:

pitch_index – A pitch index

property name: str

The name of this tuning

property zero_index: int

The zero index is a reference point, in tunings with integer indexing this is 0, in tunings with lattice indexing this is typically the zero-vector.

class xenharmlib.core.tunings.PeriodicTuning(period_length: PeriodicIndexT, eq_ratio: FrequencyRatio, *, pitch_cls: type[PeriodicPitchT], pitch_interval_cls: type[PeriodicIntervalT], pitch_scale_cls: type[PeriodicScaleT], pitch_interval_seq_cls: type[PeriodicIntervalSeqT], pitch_interval_fan_cls: type[PeriodicIntervalFanT], pitch_seq_cls: type[PeriodicSeqT], ref_frequency: Frequency)

This abstract class makes the assumption that the tuning has a period (a fixed distance between two pitches that declares the two pitches as ‘equivalent’). This can be the octave in EDO tunings or a tritave in ED3 tunings.

Periodic tunings implement the eq_diff attribute that returns the pitch difference of the equality interval:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> edo12.eq_diff
12

The constructor arguments are:

Parameters:
  • period_length – The number of pitches that constitute a period (for example 12 in 12EDO)

  • eq_ratio – The frequency factor defining the base interval (e.g. 2 for an octave, 3/2 for a fifth)

Mandatory keyword-only argument

Parameters:

ref_frequency – A reference frequency on which this tuning is built (i.e. the frequency of the zero element)

The following optional keyword-only arguments are only relevant if you are building your own tuning class:

Parameters:
  • pitch_cls – The python class that is used for the pitch object returned from the pitch method.

  • pitch_interval_cls – The python class that is used for the pitch interval object returned from the interval builder methods.

  • pitch_scale_cls – The python class that is used for the pitch scale object returned from the scale builder methods.

  • pitch_interval_seq_cls – The python class that is used for the pitch interval sequence object returned from the interval sequence builder methods.

  • pitch_interval_fan_cls – The python class that is used for the pitch interval fan object returned from the interval fan builder methods.

  • pitch_seq_cls – The python class that is used for the pitch sequence object returned from the sequence builder methods.

property eq_interval: PeriodicIntervalT

The equivalency interval of this tuning

property eq_ratio: FrequencyRatio

The frequency ratio defining the equivalency interval

pc_scale(pc_indices: List[PeriodicIndexT] | None = None, root_bi_index: int = 0) PeriodicScaleT

Constructs a pitch scale from a list of pitch class indices. The pitch class indices are assumed to be in the order they appear in the scale meaning that e.g. in 12-EDO the provided argument [7, 3, 4] will result in a scale with pitch indices [7, 15, 16]. The base interval of the first provided pc index will always assumed to be 0.

Raises:

InvalidPitchClassIndex – If one of the indices in the list is not a valid pitch class index in this tuning

Parameters:
  • pc_indices – A list of pitch class indices.

  • root_bi_index – Base interval index of the root (optional, defaults to 0)

class xenharmlib.core.tunings.SDPeriodicTuningMixin

Mixin class for single-dimensional periodic tunings. Implements various methods that are only defined in the one-dimensional case.

property generator_pitches: List[PeriodicPitchT]

Returns a list of pitch objects that can be used to generate the complete set of pitches in this tuning by subsequent interval additions with themselves.

A typical generator pitch in 12-EDO for example is the pitch with index 7 which generates the circle of fifths.

get_ring_number(pitch: PeriodicPitchT) int

Returns the greatest common divisor of a pitch and the period length of the tuning.

Parameters:

pitch – A pitch of this tuning.

class xenharmlib.core.tunings.SDTuningMixin

Mixin class for single-dimensional tunings. Implements various methods that are only defined in the one-dimensional case.

closest_freq_repr(frequency: Frequency) PitchLike

Returns the pitch in the tuning that is closest to a given frequency

Parameters:

frequency – The frequency in Hz

closest_interval(frequency_ratio: FrequencyRatio) PitchIntervalLike

Returns the interval in the tuning that is closest to a given frequency ratio.

Parameters:

frequency_ratio – The frequency ratio

closest_interval_fan(frequency_ratios: Iterable[FrequencyRatio])

Returns the interval fan in the tuning that is closest to a given iterable of frequency ratios

Parameters:

frequency_ratios – An iterable of frequency ratios

closest_interval_seq(frequency_ratios: Iterable[FrequencyRatio])

Returns the interval sequence in the tuning that is closest to a given iterable of frequency ratios

Parameters:

frequency_ratios – An iterable of frequency ratios

closest_scale(frequencies: Iterable[Frequency])

Returns the scale in the tuning that is closest to a given iterable of frequencies

Parameters:

frequencies – An iterable of frequencies in Hz

closest_seq(frequencies: Iterable[Frequency])

Returns the pitch sequence in the tuning that is closest to a given iterable of frequencies

Parameters:

frequencies – An iterable of frequencies in Hz

get_approx_pitch(frequency: Frequency) PitchLike

Deprecated since version 0.4.0: Use closest_freq_repr() instead.

Returns the closest pitch in the tuning to a given frequency.

Parameters:

frequency – The frequency in Hz

pitch_range(start, stop=None, step=1)

Returns a generator for continuous pitches of this tuning similar to pythons range function. The method can be called in the familiar ways:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> for pitch in edo12.pitch_range(3):
...    print(pitch)
EDOPitch(0, 12-EDO)
EDOPitch(1, 12-EDO)
EDOPitch(2, 12-EDO)
>>> for pitch in edo12.pitch_range(5, 10):
...    print(pitch)
EDOPitch(5, 12-EDO)
EDOPitch(6, 12-EDO)
EDOPitch(7, 12-EDO)
EDOPitch(8, 12-EDO)
EDOPitch(9, 12-EDO)
>>> for pitch in edo12.pitch_range(5, 10, 2):
...    print(pitch)
EDOPitch(5, 12-EDO)
EDOPitch(7, 12-EDO)
EDOPitch(9, 12-EDO)
class xenharmlib.core.tunings.TuningABC(*, pitch_cls: type[PitchT], pitch_interval_cls: type[IntervalT], pitch_scale_cls: type[ScaleT], pitch_interval_seq_cls: type[IntervalSeqT], pitch_interval_fan_cls: type[IntervalFanT], pitch_seq_cls: type[SeqT], ref_frequency: Frequency)

The most abstract tuning class and the base class for all other tunings. AbstractTuning makes next to no assumptions about the tuning, only that it has a reference frequency to ‘center’ the tuning and python classes that define the type of pitch, pitch interval, and pitch scale adjacent to this tuning.

A simple tuning can be derived from this simply by overwriting the method get_frequency_for_index() and and setting appropriate constructor arguments.

Mandatory keyword-only argument:

Parameters:

ref_frequency – Frequency of the zero pitch (pitch with pitch index 0, or in case of lattice points the 0 vector)

Optional keyword-only arguments: (only relevant if you want to develop your own custom tuning class inheriting from this class)

Parameters:
  • pitch_cls – The python class that is used for the pitch object returned from the pitch method.

  • pitch_interval_cls – The python class that is used for the pitch interval object returned from the interval builder methods.

  • pitch_scale_cls – The python class that is used for the pitch scale object returned from the scale builder methods.

  • pitch_interval_seq_cls – The python class that is used for the pitch interval sequence object returned from the interval sequence builder methods.

  • pitch_interval_fan_cls – The python class that is used for the pitch interval fan object returned from the interval fan builder methods.

  • pitch_seq_cls – The python class that is used for the pitch sequence object returned from the sequence builder methods.

abstractmethod get_frequency_for_index(pitch_index: IndexT) Frequency

(Must be overwritten by subclasses) Returns the frequency for a given pitch index

index_scale(pitch_indices: Iterable[IndexT] | None = None) ScaleT

Constructs a pitch scale from an iterable of pitch indices. According to the definition of a scale indices occuring multiple times will only be considered once. The list of indices will also be sorted automatically.

Parameters:

pitch_indices – An iterable of pitch indices

index_seq(pitch_indices: Iterable[IndexT] | None = None) SeqT

Constructs a pitch sequence from an iterable of pitch indices.

Parameters:

pitch_indices – An iterable of pitch indices

pitch(pitch_index: IndexT) PitchT

Returns a pitch having the pitch type this tuning was configured with

Parameters:

pitch_index – An integer denoting the number of steps from the zero pitch.

pitch_interval(pitch_a: PitchT, pitch_b: PitchT) IntervalT

Deprecated since version 0.2.0: Use interval() instead.

Returns a pitch interval having the pitch intervals type this tuning was configured with

Parameters:
  • pitch_a – The starting pitch

  • pitch_b – The target pitch

pitch_scale(pitches: List[PitchT] | None = None) ScaleT

Deprecated since version 0.2.0: Use scale() instead.

Returns a pitch scale having the pitch scale type this tuning was configured with

Parameters:

pitches – A list of pitches

property zero_element: PitchT

The zero element is a reference point, in one-dimensional tunings it is the element with index 0, in western notation typically C-0, etc

Pitch and PitchInterval

class xenharmlib.core.pitch.EDOPitch(tuning, frequency, pitch_index: PeriodicIndexT)

The pitch type for ‘equal division of the octave’ tunings

Parameters:
  • tuning – The tuning to which this pitch belongs

  • frequency – The frequency this pitch represents

  • pitch_index – An integer denoting the pitch (with 0 being the first pitch, 1 being the second, etc)

class xenharmlib.core.pitch.EDOPitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)

Pitch intervals class for ‘equal division of the octave’ pitches

class xenharmlib.core.pitch.EDPitch(tuning, frequency, pitch_index: PeriodicIndexT)

The pitch type for equal division tunings

Parameters:
  • tuning – The tuning to which this pitch belongs

  • frequency – The frequency this pitch represents

  • pitch_index – An integer denoting the pitch (with 0 being the first pitch, 1 being the second, etc)

class xenharmlib.core.pitch.EDPitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)

Pitch interval class for equal division tunings

class xenharmlib.core.pitch.PeriodicPitch(tuning, frequency, pitch_index: PeriodicIndexT)

The pitch type for periodic tunings. Depending on the period length it will classify the pitch into a ‘pitch class index’ (pc_index attribute) and a ‘base interval index’ (bi_index)

Parameters:
  • tuning – The tuning to which this pitch belongs

  • frequency – The frequency this pitch represents

  • pitch_index – An integer denoting the pitch (with 0 being the first pitch, 1 being the second, etc)

property bi_index: int

The base interval index of this pitch

is_equivalent(other: PeriodicPitchLike) bool

Returns True if this pitch has the same frequency as the other object when normalized to the first base interval

Parameters:

other – Another periodic pitch or note

property pc_index: PeriodicIndexT

The pitch class index of this pitch

pcs_normalized() Self

Returns the equivalent of this pitch in the first base interval

transpose_bi_index(bi_diff: int) Self

Returns a pitch with the same pitch class index but a transposed base interval

Parameters:

bi_diff – The difference in base interval between this pitch and the resulting one

class xenharmlib.core.pitch.PeriodicPitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)

The pitch interval class for periodic tunings.

property ic_index: PeriodicIndexT

Returns the interval class index of this interval (often simply called “interval class” or “ic”) as known from pitch class set theory.

The interval class is the shortest distance in pitch class space between two unordered pitch classes.

It is calculated by comparing the absolute value of the simple portion of the interval and its inversion, returning whatever pitch difference is smaller, so e.g. in 12-EDO the ic class for P5 is min(7, 5) = 5

ic_normalized() Self

Returns an interval class normalized version of this interval. The interval normalized version is calculated by converting (if necessary) this interval into a simple interval, normalizing it to a upwards interval and then building the minimum from that result and its inversion.

inversion() Self

Returns the inversion of this interval. The inversion is calculated by subtracting this interval from the equivalency interval.

property is_compound: bool

Returns True if this interval is a compound interval, False otherwise. A compound interval is defined as an interval whose absolute value is strictly greater than the equivalency interval (meaning the equivalency interval itself and its negation are not considered compound)

property is_simple: bool

Returns True if this interval is a simple interval, False otherwise. A simple interval is defined as an interval whose absolute value is lesser or equal than the equivalency interval (meaning the equivalency interval itself and its negation are considered a simple interval)

to_simple() Self

Returns the corresponding simple interval if this is a compound interval (or the interval itself if it is already simple)

The method preserves direction, so if this interval is a downward interval, the resulting interval will also be a downward interval.

class xenharmlib.core.pitch.Pitch(tuning, frequency, pitch_index: IndexT)

In its most basic form, a Pitch is a tuple of a pitch index (an integer value) and a tuning that interprets this index as a frequency.

Pitch creates a total ordering on all pitches according to their frequency. This means you can sort pitches in lists (e.g. from lowest frequency to highest frequency). You can also compare pitches, even across different tunings:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> edo31 = EDOTuning(31)
>>> edo31.pitch(1) < edo12.pitch(1)
True
>>> edo31.pitch(31) == edo12.pitch(12)
True
Parameters:
  • tuning – The tuning to which this pitch belongs

  • frequency – The frequency this pitch represents

  • pitch_index – An integer denoting the pitch (with 0 being the first pitch, 1 being the second, etc)

retune(tuning: SDTuningLike) Self

Deprecated since version 0.4.0: Use retune_closest() instead.

Gets the frequency representation in a given origin context that is closest to the frequency of this object.

Parameters:

origin_context – The target origin context

Raises:

TypeError – If the target tuning does not have a proper definition of a closest representation to a given frequency

property short_repr: str

(Must be implemented by subclasses) A shortened representation of this note (to be used in collection objects like scales)

transpose(diff: IndexT | PitchInterval) Pitch

Transposes the pitch to a different one

Parameters:

diff – The difference from this pitch. Can be either an integer (positive for upward movement, negative for downward movement) or a pitch interval

property tuning

The origin tuning of this pitch

class xenharmlib.core.pitch.PitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)

The most abstract form of an interval of two pitches. Implements conversion functions to frequency ratios and a total ordering based on the calculated ratios:

>>> from xenharmlib import EDOTuning
>>> edo31 = EDOTuning(31)
>>> pitch_a = edo31.pitch(4)
>>> pitch_b = edo31.pitch(8)
>>> pitch_c = edo31.pitch(10)
>>> i_ab = pitch_a.interval(pitch_b)
>>> i_ac = pitch_a.interval(pitch_c)
>>> i_ab < i_ac
True

A caveat: Intervals are considered directional in xenharmlib so the order of pitches from which the interval is created is important

Parameters:
  • tuning – The tuning associated with this interval

  • frequency_ratio – The frequency ratio of this interval

  • pitch_diff – An integer that defines the number of steps this interval encompasses (a positive integer means ‘upward steps’, while a negative one means ‘downward steps’)

  • ref_pitch – A reference pitch for the pitch difference. This is necessary for tunings that are not equal step.

classmethod from_pitches(pitch_a: PitchT, pitch_b: PitchT) Self

Deprecated since version 0.2.0: Use from_source_and_target() instead.

Constructs an interval out of two pitches of the same tuning. If the second pitch is lower than the first pitch the Interval will have a negative pitch difference

Raises:

IncompatibleOriginContexts – If pitches come from different tuning systems

Parameters:
  • pitch_a – The first (or reference) pitch

  • pitch_b – The second (or target) pitch

classmethod from_source_and_target(source: PitchT, target: PitchT) Self

Constructs an interval out of two pitches of the same tuning. If the second pitch is lower than the first pitch the Interval will have a negative pitch difference

Raises:

IncompatibleOriginContexts – If pitches come from different tuning systems

Parameters:
  • source – The starting point of the interval

  • target – The end point of the interval

property short_repr: str

A short string representation of the interval (must be implemented by subclass)

PitchScale

A pitch scale is an ordered set of unique pitches in a given tuning. The uniqueness property means that there are no duplicate pitches. However other than in the popular use of the word ‘scale’ the pitch scale object in xenharmlib is not limited to one base interval in periodic tunings. (e.g. C-0 and C-1 are considered distinct) This has a couple of advantages, e.g. that the scale object can be used more generally.

Pitch scales have both a list and a set quality to them. Similar to lists they have an item order, support iteration, positional item retrieval, and slicing. At the same time scales support set operations like intersection, union, symmetric difference, etc.

class xenharmlib.core.pitch_scale.EDOPitchScale(tuning, pitches: Iterable[PitchT] | None = None)

Pitch scale class for ‘equal division of the octave’ tunings

class xenharmlib.core.pitch_scale.EDPitchScale(tuning, pitches: Iterable[PitchT] | None = None)

Pitch scale class for equal division tunings

class xenharmlib.core.pitch_scale.PeriodicPitchScale(tuning, pitches: Iterable[PitchT] | None = None)

Pitch scale class for periodic tunings. Implements operations like rotation and customized set operations (for when you want to treat equivalent pitches the same as equal pitches). It also implements normalization methods.

property pc_indices: List[PeriodicIndexT]

Returns a list of pitch class indices in the order they appear in this scale. This can include duplicate items if the list has two pitches of the same pitch class

class xenharmlib.core.pitch_scale.PitchScale(tuning, pitches: Iterable[PitchT] | None = None)

The base class of all pitch scales. Implements list and set operations, transposition, retuning, etc.

PitchScale (or, respectively, its subclasses) are built by the tunings pitch_scale builder method:

>>> from xenharmlib import EDOTuning
>>> edo31 = EDOTuning(31)
>>> scale = edo31.scale(
...     [edo31.pitch(4), edo31.pitch(6), edo31.pitch(9)]
... )

Every pitch will be automatically sorted in its place. The order of the scale is ascending (First lower pitch, then higher pitch)

PitchScale objects support most of the typical list operations:

>>> for pitch in scale:
...     print(pitch)
EDOPitch(4, 31-EDO)
EDOPitch(6, 31-EDO)
EDOPitch(9, 31-EDO)
>>> scale[1]
EDOPitch(6, 31-EDO)
>>> scale[1:-1]
EDOPitchScale([6], 31-EDO)

The ‘in’ operator accepts both pitches and pitch intervals

>>> p = edo31.pitch(4)
>>> p in scale
True
>>> p.interval(edo31.pitch(2)) in scale
True

In regards to intervals, it even works across tunings

>>> edo12 = EDOTuning(12)
>>> edo24 = EDOTuning(24)
>>> edo12_fifth = edo12.pitch(0).interval(edo12.pitch(7))
>>> edo24_scale = edo24.scale(edo24.pitch_range(24))
>>> edo12_fifth in edo24_scale
True

In addition similar operations to the native python sets are available (with slightly different naming and additional method arguments):

  • union

  • intersection

  • difference

  • symmetric_difference

  • is_disjoint

  • is_subset

  • is_superset

add_pitch(pitch: PitchT)

Deprecated since version 0.2.0: objects in xenharmlib are supposed to be immutable

Inserts a new pitch into the scale at the right position

Raises:

IncompatibleOriginContexts – If the pitch has a different tuning than this scale.

Parameters:

pitch – The new pitch

add_pitch_index(pitch_index: IndexT)

Deprecated since version 0.2.0: objects in xenharmlib are supposed to be immutable

Inserts a new pitch into the scale denoted by its pitch index

Parameters:

pitch_index – Index of the pitch

classmethod from_pitch_indices(pitch_indices: List[IndexT], tuning) Self

Deprecated since version 0.3.0: Use index_scale() instead.

Creates a scale from a list of pitch indices

Parameters:
  • pitch_indices – A list of pitch indices in any order.

  • tuning – The tuning through which these indices should be interpreted

property is_zero_normalized: bool

Returns True if this function is zero normalized, meaning that the first element of the scale is identical to the pitch with index 0

property pitch_indices: List[IndexT]

A list of the ordered pitch indices present in this scale

retune(tuning) PitchScale

Deprecated since version 0.4.0: Use retune_closest() instead.

Returns a scale retuned into a different tuning by approximating every pitch in the scale with a pitch from the target tuning.

A caveat: Since pitch scales are a structure of sorted unique pitches this method may produce a scale with a smaller size than the original because two pitches in this tuning can be approximated to the same pitch in the target tuning.

Parameters:

tuning – The target tuning

to_pitch_intervals() List[Interval[PitchT]]

Deprecated since version 0.2.0: Use to_interval_seq() instead.

Returns this scale represented as a list of pitch intervals

transpose(diff: IndexT | PitchInterval[IndexT, PitchT]) Self

Transposes the scale upwards or downwards

Parameters:

diff – The difference from this pitch. Can be either a pitch index difference (positive for upward movement, negative for downward movement) or a pitch interval

Multi-Generator Tunings

This module implements tunings and musical objects relating to multi-generator tunings.

A multi-generator tuning describes the frequency space as a lattice in which each integer vector \((x_1, ..., x_n)\) represents the exponents in the expression g_1^{x_1} cdot … cdot g_n^{x_n} built from a generator vector \((g_1, ..., g_n)\)

Multi-generator tunings are an abstraction of JI prime limit tuning classes, that can hold arbitrary generators. Because of this they have multiple applications in temperament theory.

class xenharmlib.core.multigen.MultiGenPitch(tuning, frequency, pitch_index: PeriodicIndexT)
property short_repr: str

(Must be implemented by subclasses) A shortened representation of this note (to be used in collection objects like scales)

class xenharmlib.core.multigen.MultiGenPitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)
property short_repr: str

A short string representation of the interval (must be implemented by subclass)

class xenharmlib.core.multigen.MultiGenPitchIntervalFan(tuning, intervals: Iterable[PitchIntervalT] | None = None)
class xenharmlib.core.multigen.MultiGenPitchIntervalSeq(tuning, intervals: Iterable[PitchIntervalT] | None = None)
class xenharmlib.core.multigen.MultiGenPitchScale(tuning, pitches: Iterable[PitchT] | None = None)
class xenharmlib.core.multigen.MultiGenPitchSeq(tuning, elements: Iterable[PitchT] | None = None)
class xenharmlib.core.multigen.MultiGenTuning(generators: ~typing.Tuple[~xenharmlib.core.frequencies.FrequencyRatio, ...], eq_diff_vec: ~typing.Tuple[int, ...], ref_frequency: ~xenharmlib.core.frequencies.Frequency = Frequency(<Mock name='mock.Rational()' id='130468837690256'>), pitch_cls: type[~xenharmlib.core.multigen.MultiGenPitchT] = <class 'xenharmlib.core.multigen.MultiGenPitch'>, pitch_interval_cls: type[~xenharmlib.core.multigen.MultiGenIntervalT] = <class 'xenharmlib.core.multigen.MultiGenPitchInterval'>, pitch_scale_cls: type[~xenharmlib.core.multigen.MultiGenScaleT] = <class 'xenharmlib.core.multigen.MultiGenPitchScale'>, pitch_interval_seq_cls: type[~xenharmlib.core.multigen.MultiGenIntervalSeqT] = <class 'xenharmlib.core.multigen.MultiGenPitchIntervalSeq'>, pitch_interval_fan_cls: type[~xenharmlib.core.multigen.MultiGenIntervalFanT] = <class 'xenharmlib.core.multigen.MultiGenPitchIntervalFan'>, pitch_seq_cls: type[~xenharmlib.core.multigen.MultiGenSeqT] = <class 'xenharmlib.core.multigen.MultiGenPitchSeq'>)

Base class for multi-generator tunings. A multi-generator tuning describes the frequency space as a lattice in which each integer vector \((x_1, ..., x_n)\) represents the exponents in the expression g_1^{x_1} cdot … cdot g_n^{x_n} built from a generator vector \((g_1, ..., g_n)\)

Parameters:
  • generators – A tuple of frequency ratios that constitutes the generator vector of this tuning

  • eq_diff_vec – A vector of integers that defines the pitch difference of the interval that should be considered the equivalence interval, so for example in a pythagorean tuning with generators 2 and 3, this should be (1, 0) for the octave.

  • ref_frequence – A reference frequency for the zero index (optional, defaults to the frequency for C0 in EDO tunings for A4 = 440 Hz (about 16.35 Hz))

closest_freq_repr(frequency: Frequency)

Returns the frequency representation closest to a given frequency. Raises TypeError in here, because a closest frequency representation does not exist in the general case for multi-generator tunings.

closest_interval(frequency_ratio: FrequencyRatio)

Returns the interval closest to a given frequency ratio Raises TypeError in here, because a closest interval does not exist in the general case for multi-generator tunings.

closest_interval_fan(frequency_ratios: Iterable[FrequencyRatio])

Returns the interval fan closest to a given iterable of frequency ratios. Raises TypeError in here, because a closest interval sequence does not exist in the general case for multi-generator tunings.

closest_interval_seq(frequency_ratios: Iterable[FrequencyRatio])

Returns the interval sequence closest to a given iterable of frequency ratios. Raises TypeError in here, because a closest interval sequence does not exist in the general case for multi-generator tunings.

closest_scale(frequencies: Iterable[Frequency])

Returns the scale closest to a given iterable of frequencies Raises TypeError in here, because a closest scale does not exist in the general case for multi-generator tunings.

closest_seq(frequencies: Iterable[Frequency])

Returns the sequence closest to a given iterable of frequencies Raises TypeError in here, because a closest scale does not exist in the general case for multi-generator tunings.

diff_interval(pitch_diff: LatticePoint) MultiGenIntervalT

Returns an interval the size of a given pitch index difference.

Parameters:

pitch_diff – The pitch index difference

get_frequency_for_index(pitch_index: LatticePoint) Frequency

Returns the frequency for a given pitch index

property lattice: Lattice

The lattice on which this tuning is based upon

vec_interval(vector: Tuple[int, ...]) MultiGenIntervalT

Convenience function to create an interval from an integer vector defining the exponents of the generators of this tuning, so for example in a pythagorean tuning with generators 2 and 3 input parameter (-1, 1) produces the perfect fifth interval

Parameters:

vector – An integer tuple

vec_interval_fan(vectors: Iterable[Tuple[int, ...]] | None = None) MultiGenIntervalFanT

Convenience function to create an interval fan from an iterable of integer vectors defining all the exponents of the generators of each respective interval in the sequence, so for example in a pythagorean tuning with generators 2 and 3 the value [(0, 0), (-7, 4), (-1, 1)] produces the interval fan of the major triad.

Parameters:

vectors – An iterable of integer tuples

vec_interval_seq(vectors: Iterable[Tuple[int, ...]] | None = None) MultiGenIntervalSeqT

Convenience function to create an interval sequence from an iterable of integer vectors defining all the exponents of the generators of each respective interval in the sequence, so for example in a pythagorean tuning with generators 2 and 3 the value [(-7, 4), (6, -3)] produces the interval sequence of the major triad.

Parameters:

vectors – An iterable of integer tuples

vec_pitch(vector: Tuple[int, ...]) MultiGenPitchT

Convenience function to create a pitch from an integer vector defining the exponents of the generators of this tuning, so for example in a pythagorean tuning with generators 2 and 3 input parameter (-1, 1) produces the pitch equivalent to the note G0

Parameters:

vector – An integer tuple

vec_scale(vectors: Iterable[Tuple[int, ...]] | None = None) MultiGenScaleT

Convenience function to create a scale from an iterable of integer vectors defining all the exponents of the generators of each respective pitch in the scale, so for example in a pythagorean tuning with generators 2 and 3 the value [(0, 0), (-7, 4), (-1, 1)] produces the C0 major triad.

Parameters:

vectors – An iterable of integer tuples

vec_seq(vectors: Iterable[Tuple[int, ...]] | None = None) MultiGenSeqT

Convenience function to create a sequence from an iterable of integer vectors defining all the exponents of the generators of each respective pitch in the sequence, so for example in a pythagorean tuning with generators 2 and 3 the value [(0, 0), (-7, 4), (-1, 1)] produces the C0 major triad sequence

Parameters:

vectors – An iterable of integer tuples

property zero_index: LatticePoint

The lattice point representing the zero index

Prime-Limit Tunings

This module implements tunings in which pitches and intervals are formed by using consecutive prime numbers as generators up until to a specified prime (the “prime limit”).

class xenharmlib.core.primelimit.PrimeLimitPitch(tuning, frequency, pitch_index: PeriodicIndexT)

The pitch type for prime limit tunings

Parameters:
  • tuning – The tuning to which this pitch belongs to

  • frequency – The frequency this pitch represents

  • pitch_index – An lattice point denoting the pitch

property monzo: Tuple[int, ...]

Returns the monzo of this pitch. A monzo is a tuple of exponents for the prime number generator vector, e.g. in 5-Limit for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

property pc_short_repr: str

A shortened representation string of the pitch class ratio of this pitch

property short_repr: str

A shortened representation string of this pitch without any type prefix

class xenharmlib.core.primelimit.PrimeLimitPitchInterval(tuning, frequency_ratio: FrequencyRatio, pitch_diff: IndexT, ref_pitch: PitchT)

The pitch interval type for prime limit tunings

Parameters:
  • tuning – The tuning associated with this interval

  • frequency_ratio – The frequency ratio of this interval

  • pitch_diff – An integer that defines the number of steps this interval encompasses (a positive integer means ‘upward steps’, while a negative one means ‘downward steps’)

  • ref_pitch – A reference pitch for the pitch difference. This is necessary for tunings that are not equal step.

property monzo: Tuple[int, ...]

Returns the monzo of this pitch. A monzo is a tuple of exponents for the prime number generator vector, e.g. in 5-Limit for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

property short_repr: str

A shortened representation string of the frequency ratio of this interval

class xenharmlib.core.primelimit.PrimeLimitPitchIntervalFan(tuning, intervals: Iterable[PitchIntervalT] | None = None)

The pitch interval fan type for prime limit tunings

property monzos: List[Tuple[int, ...]]

Returns the monzos of the intervals in this interval fan. A monzo is a tuple of exponents for the prime number generator vector, e.g. in a 5-Limit tuning fan for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

to_ec_expr() str

Transforms this interval fan into an enumerated chord expression e.g. [1/1, 5/4, 3/2] into ‘4:5:6’

An enumerated chord expression is a short form to notate frequency ratios of an interval fan as a sequence of integers where each integer is divided by the first integer.

Raises:
  • ValueError – If fan includes less than two intervals

  • ValueError – If fan does not start with unison interval

class xenharmlib.core.primelimit.PrimeLimitPitchIntervalSeq(tuning, intervals: Iterable[PitchIntervalT] | None = None)

The pitch interval sequence type for prime limit tunings

property monzos: List[Tuple[int, ...]]

Returns the monzos of the intervals in this interval sequence. A monzo is a tuple of exponents for the prime number generator vector, e.g. in a 5-Limit tuning sequence for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

class xenharmlib.core.primelimit.PrimeLimitPitchScale(tuning, pitches: Iterable[PitchT] | None = None)

The pitch scale type for prime limit tunings

property monzos: List[Tuple[int, ...]]

Returns the monzos of the pitches in this scale. A monzo is a tuple of exponents for the prime number generator vector, e.g. in a 5-Limit tuning scale for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

class xenharmlib.core.primelimit.PrimeLimitPitchSeq(tuning, elements: Iterable[PitchT] | None = None)

The pitch sequence type for prime limit tunings

property monzos: List[Tuple[int, ...]]

Returns the monzos of the pitches in this sequence. A monzo is a tuple of exponents for the prime number generator vector, e.g. in a 5-Limit tuning sequence for 5/4 = 2**(-2) * 3**(0) * 5**(1) the monzo is (-2, 0, 1)

class xenharmlib.core.primelimit.PrimeLimitTuning(prime_limit: int, eq_diff_vec: Tuple[int, ...] | None=None, *, ref_frequency: Frequency = Frequency(<Mock name='mock.Rational()' id='130468837690256'>), pitch_cls: type[PrimeLimitPitchT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitch'>, pitch_interval_cls: type[PrimeLimitIntervalT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitchInterval'>, pitch_scale_cls: type[PrimeLimitScaleT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitchScale'>, pitch_interval_seq_cls: type[PrimeLimitIntervalSeqT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitchIntervalSeq'>, pitch_interval_fan_cls: type[PrimeLimitIntervalFanT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitchIntervalFan'>, pitch_seq_cls: type[PrimeLimitSeqT] = <class 'xenharmlib.core.primelimit.PrimeLimitPitchSeq'>)

A prime limit tuning is a multi-generator tuning in which the generator vector consists of consecutive primes up to a certain point (the prime limit), so for example a 7-Limit tuning spans a frequency space with generators (2, 3, 5, 7)

Parameters:
  • prime_limit – The last prime number in the sequence of consecutive prime numbers which form the generator vector

  • ref_frequency – A reference frequency defining the frequency of the zero index (optional, defaults to the frequency of C0 for A4=440 in 12-EDO)

  • eq_diff_vec – (optional) A vector of integers that defines the pitch difference of the interval that should be considered the equivalence interval, so for example in a 5 limit tuning with generators (2, 3, 5), this should be (1, 0, 0) for the octave, (0, 1, 0) for the tritave, etc. If parameter is omitted the octave is assumed to be the equivalence interval

ec_interval_fan(expr: str) PrimeLimitIntervalFanT

Creates an interval fan from an enumerated chord expression, e.g. ‘4:5:6’ ^= [4/4, 5/4, 6/4] ^= [1/1, 5/4, 3/2]

An enumerated chord expression is a short form to notate frequency ratios of an interval fan as a sequence of integers where each integer is divided by the first integer.

Parameters:

expr – An expression of the form ‘4:5:6’

Raises:
  • ValueError – If expression string is invalid

  • ValueError – If expression contains a number that violates the prime limit of this tuning

property prime_limit: int

The highest prime number in the sequence of consecutive prime number frequency ratios that forms the generator vector

ratio_interval(frequency_ratio: FrequencyRatio) PrimeLimitIntervalT

Convenience function to create an interval from a frequency ratio. The frequency ratio must be an element of the rational numbers and its biggest prime factor must not exceed the prime limit of this tuning.

Parameters:

frequency_ratio – A frequency ratio object

Raises:

ValueError – If biggest prime factor exceeds the limit

ratio_interval_fan(frequency_ratios: Iterable[FrequencyRatio] | None = None) PrimeLimitIntervalFanT

Convenience function to create an interval fan from an iterable of frequency ratios. The frequency ratios must all be elements of the rational numbers and their biggest prime factors must not exceed the prime limit of this tuning.

Parameters:

frequency_ratios – An iterable of frequency ratio objects

Raises:

ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

ratio_interval_seq(frequency_ratios: Iterable[FrequencyRatio] | None = None) PrimeLimitIntervalSeqT

Convenience function to create an interval sequence from an iterable of frequency ratios. The frequency ratios must all be elements of the rational numbers and their biggest prime factors must not exceed the prime limit of this tuning.

Parameters:

frequency_ratios – An iterable of frequency ratio objects

Raises:

ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

ratio_pc_scale(pc_frequency_ratios: Iterable[FrequencyRatio] | None = None, root_bi_index: int = 0) PrimeLimitScaleT

Constructs a scale from a list of frequency ratios representing successive pitch classes (meaning all ratios have to be between the unison ratio (1/1) and the equivalency ratio). The pitch class ratios are assumed to be in the order they appear in the scale, meaning that the following expression will result in the G-B-D triad:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> scale = tuning.ratio_pc_scale(
...    [
...        FrequencyRatio(3, 2),
...        FrequencyRatio(15, 8),
...        FrequencyRatio(10, 9)
...    ],
... )
Parameters:
  • ratio_strs – A list of ratio string expressions

  • root_bi_index – The base interval index of the root pitch (optional, defaults to 0)

Raises:
  • ValueError – If one of the frequency ratios is not between the unison ratio and the equivalency ratio

  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

ratio_pitch(frequency_ratio: FrequencyRatio) PrimeLimitPitchT

Convenience function to create a pitch from a frequency ratio defining the resulting pitch by the interval from the zero element.

The frequency ratio must be an element of the rational numbers and its biggest prime factor must not exceed the prime limit of this tuning.

Parameters:

frequency_ratio – A frequency ratio object

Raises:

ValueError – If biggest prime factor exceeds the limit

ratio_scale(frequency_ratios: Iterable[FrequencyRatio] | None = None) PrimeLimitScaleT

Convenience function to create a scale from an iterable of frequency ratios, all refering to the interval distance of the scale pitches to the zero element, e.g. in 5 limit tuning, creating a major chord on C1 can be done like this:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> scale = tuning.ratio_scale(
...    [FrequencyRatio(2, 1), FrequencyRatio(5, 2), FrequencyRatio(3)],
... )
>>> scale
PrimeLimitPitchScale([2, 5/2, 3], 5-Limit)
Parameters:

frequency_ratios – An iterable of frequency ratio objects

Raises:

ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

ratio_seq(frequency_ratios: Iterable[FrequencyRatio] | None = None) PrimeLimitSeqT

Convenience function to create a sequence from an iterable of frequency ratios, all refering to the interval distance of the sequence pitches to the zero element, e.g. in 5 limit tuning, creating a major chord sequence on C1 can be done like this:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> scale = tuning.ratio_seq(
...    [FrequencyRatio(2, 1), FrequencyRatio(5, 2), FrequencyRatio(3)],
... )
>>> scale
PrimeLimitPitchSeq([2, 5/2, 3], 5-Limit)
Parameters:

frequency_ratios – An iterable of frequency ratio objects

Raises:

ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

rs_interval(ratio_str: str) PrimeLimitIntervalT

Convenience function to create an interval from a frequency ratio string expression. String expressions can be two natural numbers separated by a slash (e.g. ‘5/4’) or a single number (e.g. ‘3’, refering to the 3/1 ratio)

Parameters:

ratio_str – The ratio string expression

Raises:
  • ValueError – If a prime factor of the frequency ratio exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_interval_fan(ratio_strs: Iterable[str] | None = None) PrimeLimitIntervalFanT

Convenience function to create an interval fan from an iterable of frequency ratio string expressions. String expressions can be two natural numbers separated by a slash (e.g. ‘5/4’) or a single number (e.g. ‘3’, refering to the 3/1 ratio)

Parameters:

ratio_strs – A list of ratio string expressions

Raises:
  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_interval_seq(ratio_strs: Iterable[str] | None = None) PrimeLimitIntervalSeqT

Convenience function to create an interval sequence from an iterable of frequency ratio string expressions. String expressions can be two natural numbers separated by a slash (e.g. ‘5/4’) or a single number (e.g. ‘3’, refering to the 3/1 ratio)

Parameters:

ratio_strs – A list of ratio string expressions

Raises:
  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_pc_scale(ratio_strs: Iterable[str] | None = None, root_bi_index: int = 0) PrimeLimitScaleT

Constructs a scale from a list of frequency ratios given as strings representing successive pitch classes (meaning all ratios have to be between the unison ratio (1/1) and the equivalency ratio). The pitch class ratios are assumed to be in the order they appear in the scale, meaning that the following expression will result in the G-B-D triad:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> tuning.rs_pc_scale(['3/2', '15/8', '10/9'])
PrimeLimitPitchScale([3/2, 15/8, 20/9], 5-Limit)
Parameters:
  • ratio_strs – A list of ratio string expressions

  • root_bi_index – The base interval index of the root pitch (optional, defaults to 0)

Raises:
  • ValueError – If one of the frequency ratios is not between the unison ratio and the equivalency ratio

  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_pitch(ratio_str: str) PrimeLimitPitchT

Convenience function to create a pitch from a frequency ratio string expression, defining the resulting pitch by the interval from the zero element. String expressions can be two natural numbers separated by a slash (e.g. ‘5/4’) or a single number (e.g. ‘3’, refering to the 3/1 ratio)

Parameters:

ratio_str – The ratio string expression

Raises:
  • ValueError – If a prime factor of the frequency ratio exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_scale(ratio_strs: Iterable[str] | None = None) PrimeLimitScaleT

Convenience function to create a scale from an iterable of frequency ratio expressions, all refering to the interval distance of the scale pitches to the zero element, e.g. in 5 limit tuning, creating a major chord on C1 can be done like this:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> tuning.rs_scale(['2', '5/2', '3'])
PrimeLimitPitchScale([2, 5/2, 3], 5-Limit)
Parameters:

ratio_strs – A list of ratio string expressions

Raises:
  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

rs_seq(ratio_strs: Iterable[str] | None = None) PrimeLimitSeqT

Convenience function to create a sequence from an iterable of frequency ratio expressions, all refering to the interval distance of the sequence pitches to the zero element, e.g. in 5 limit tuning, creating a major chord sequence on C1 can be done like this:

>>> from xenharmlib import PrimeLimitTuning
>>>
>>> tuning = PrimeLimitTuning(5)
>>> tuning.rs_seq(['2', '5/2', '3'])
PrimeLimitPitchSeq([2, 5/2, 3], 5-Limit)
Parameters:

ratio_strs – A list of ratio string expressions

Raises:
  • ValueError – If a prime factor of one of the frequency ratios exceeds the prime limit of this tuning

  • ValueError – If expression was ill-formatted

Notation

The notation core module includes primitives to build notation systems. A notation in xenharmlib is defined as a wrapper around a specific tuning that provides a human-friendly string interface to all the lower-level objects (pitch, pitch interval, pitch scale)

exception xenharmlib.core.notation.IncompleteNotation

Gets raised when a notation is not initialized correctly

class xenharmlib.core.notation.NatAccNotation(tuning, acc_weights: ~typing.Tuple[~xenharmlib.core.notation.PeriodicIndexT, ...], *, note_cls: type[~xenharmlib.core.notes.NatAccNote] = <class 'xenharmlib.core.notes.NatAccNote'>, note_interval_cls: type[~xenharmlib.core.notes.NatAccNoteInterval] = <class 'xenharmlib.core.notes.NatAccNoteInterval'>, note_scale_cls: type[~xenharmlib.core.note_scale.NatAccNoteScale] = <class 'xenharmlib.core.note_scale.NatAccNoteScale'>, note_interval_seq_cls: type[~xenharmlib.core.note_interval_seq.NatAccNoteIntervalSeq] = <class 'xenharmlib.core.note_interval_seq.NatAccNoteIntervalSeq'>, note_interval_fan_cls: type[~xenharmlib.core.note_interval_fan.NatAccNoteIntervalFan] = <class 'xenharmlib.core.note_interval_fan.NatAccNoteIntervalFan'>, note_seq_cls: type[~xenharmlib.core.note_seq.NatAccNoteSeq] = <class 'xenharmlib.core.note_seq.NatAccNoteSeq'>)

NatAccNotation is a notation for periodic tunings that select a subset of pitches called naturals to form a basic symbol set (typically letters) and adds special symbols called accidentals, which signify step deviations from the natural pitch classes.

The standard Western notation for example is a such a notation, defining 7 naturals (C, D, E, G, A, B) and 2 accidentals (#, b) that signify a step deviation of +1 and -1 respectively.

The class supports different ‘categories’ of accidentals that do not interact with one another, for example, a category for sharps/flats and one category for ups/downs. Consequently, accidental values are given in the form of a vector.

It assumes that intervals are uniquely defined by their difference in natural index + the difference of the accidental vectors of their source notes. It further assumes that intervals are notated as a tuple (symbol, number). The class implements the 1-based ordinal notation for numbers by default (e.g. the number 1 for a unison, the number 2 for a second, etc), however this behavior get be changed by subclassing and overwriting the method nat_diff_to_interval_number() and its counterpart interval_number_to_nat_diff()

Parameters:
  • tuning – The tuning this notation refers to

  • acc_weight – A vector defining a mapping between the (unweighted) accidental sum vector and the (weighted) accidental diff vector

Additional, optional keyword-only arguments: (only relevant if you want to develop your own notation)

Parameters:
  • note_cls – The python class that is used for the note object returned from the note method.

  • note_interval_cls – The python class that is used for the note interval object returned from the interval builder methods.

  • note_scale_cls – The python class that is used for the note scale object returned from the scale builder methods.

  • note_interval_seq_cls – The python class that is used for the note interval sequence object returned from the interval sequence builder methods.

  • note_interval_fan_cls – The python class that is used for the note interval fan object returned from the interval fan builder methods.

  • note_seq_cls – The python class that is used for the note sequence object returned from the sequence builder methods.

acc_diff_vector_to_acc_sum_vector(acc_diff_vector: Tuple[PeriodicIndexT, ...]) Tuple[int, ...]

Transforms a (weighted) accidental diff vector into a (unweighted) accidental sum vector.

Raises:
  • ValueError – If dimensions of accidental diff vector do not match the weighting vector dimensions of this notation

  • ValueError – If accidental diff vector is not in the image of the accidental weight mapping of this notation

Parameters:

acc_diff_vector – The accidental diff vector

acc_sum_vector_to_acc_diff_vector(acc_sum_vector: Tuple[int, ...]) Tuple[PeriodicIndexT, ...]

Transforms an (unweighted) accidental sum vector into a (weighted) accidental diff vector.

Raises:

ValueError – If dimensions of accidental sum vector do not match the weighting vector dimensions of this notation

Parameters:

acc_sum_vector – The accidental sum vector

property acc_symbol_code: SymbolCode

The symbol code for the accidentals. Must be set by the subclass constructor

property acc_weights: Tuple[PeriodicIndexT, ...]

The weight vector that transforms the accidental sum vector into the accidental diff vector

append_natural(natc_symbol: str, natc_pitch_index: PeriodicIndexT)

Appends a new natural to this notation. The order in which naturals are added determines their natural class index, so the first added natural will get natural class index 0, the second 1, and so forth.

Raises:

AmbiguousSymbol – If the given natural symbol already denotes a previously appended natural index

Parameters:
  • natc_symbol – A string denoting the natural (typically a single letter)

  • natc_pitch_index – The pitch index of the natural class that is added. For most tunings, this pitch index is equal to the pitch class index because for most tunings there exists no note tuple (natc_symbol, 0) that is not in the first base interval. However, there are outliers like 5-EDO in which (B, 0) = (C, 1) and (B, 0) has a pitch index in the second base interval

property eq_diff: PeriodicIndexT

The pitch difference of the equivalency interval of this notation

property eq_interval: NatAccNoteInterval

The equivalency interval of this notation

gen_pc_symbol(natc_index: int, acc_sum_vector: Tuple[int, ...]) Tuple[str, str, str]

Creates a pitch class symbol from a natural class index and an accidental vector. This defaults to calculating the first natural symbol found for natc_index and concatenating it with the minimal accidental symbol configuration for the vector.

The method will return a tuple of 3 with the following semantics:
  • The first element will be the pitch class symbol

  • The second element will be the natural class symbol

  • The third element will be the accidental symbol string

(Subclasses can overwrite this method if they e.g wish to generate pc_symbols that have post-fix or in-fix naturals)

Parameters:
  • natc_index – A natural class index of a note

  • acc_sum_vector – An (unweighted) accidental sum vector

get_acc_sum_balance_vector(delta: PeriodicIndexT) Tuple[int, ...]

Returns an accidental sum balance vector for a pitch difference delta. By default this function assumes that pitch difference deltas introduced on transposition or interval determination can be counter-balanced by changing the first dimension of the accidental sum vector (e.g. B0 + P5 = F#1). If your subclassed notation does this differently you need to change this behavior by overwriting the method in your class.

Parameters:

delta – The delta in pitch difference

get_acc_symbol(acc_sum_vector: Tuple[int, ...]) str

Returns a symbol string for an accidental vector, like (1, 0) -> ‘#’ or (-1, 1) -> ‘^b’

Raises:

InvalidAccidentalValue – If the accidental symbol code of this notation does not have a symbol or symbol combination that maps to this value

Parameters:

acc_sum_vector – The (unweighted) accidental sum vector

get_interval_symbol(nat_diff: int, acc_sum_vector: Tuple[int]) str

Returns the interval symbol for a natural/accidental note interval. Interval symbols depend on the natural index difference class of the two notes (e.g. if the interval is a unison, a third, a fifth, etc) and the deviation from the standard pitch difference of a natural difference (In 12-EDO this standard pitch difference would e.g. be 7 for a fifth, 4 for a third, etc)

Raises:

IncompleteNotation – If no symbol code was registered for the given parameters

Parameters:
  • nat_diff – The difference in natural indices

  • acc_sum_vector – The (unweighted) accidental sum vector of the interval

get_natc_symbol(nat_index: int) str

Returns a string symbol for a natural index like 0 -> ‘C’ or 8 -> ‘D’ in 12-EDO

Raises:

InvalidNaturalIndex – If the natural index is smaller than 0

Parameters:

nat_index – A natural index of this notation

interval_number_to_nat_diff(interval_number: int) int

Returns a natural index difference for an interval number. By default, it assumes that the interval number is given in 1-based ordinal notation. Subclasses can change this behavior by overwriting this method.

Parameters:

interval_number – An interval number

is_natural(pitch_index: PeriodicIndexT) bool

Returns True if the given pitch index refers to a natural in this notation, False otherwise

Parameters:

pitch_index – The pitch index to consider

property nat_count: int

Returns the number of registered natural symbols for this notation (typically 7 for Western-style notations)

nat_diff_to_interval_number(nat_diff: int) int

Returns an interval number for a natural index difference. By default, it returns a 1-based ordinal number. Subclasses can change this behavior by overwriting this method.

Parameters:

nat_diff – The natural index difference that characterizes the interval

nat_index_to_pc_index(nat_index: int) PeriodicIndexT

Returns the pitch class index a natural index refers to

Parameters:

nat_index – A natural index

nat_index_to_pitch_index(nat_index: int) PeriodicIndexT

Returns the pitch index a natural index refers to (e.g. in 12-EDO: 0 -> 0, 1 -> 2, 3 -> 4, 4 -> 5)

Parameters:

nat_index – A natural index

property natc_pc_indices: List[PeriodicIndexT]

A sorted list of natural class pitch class indices that are present in this notation

property natc_pitch_indices: List[PeriodicIndexT]

A sorted list of natural class pitch indices that are present in this notation

natural_scale(bi_index: int = 0) NatAccNoteScale

Creates a scale with all the naturals in this notation in a specific base interval (in Western notations this is typically the C major scale)

Parameters:

bi_index – (optional, defaults to 0). The index of the base interval the notes should reside in

note(pc_symbol: str, nat_bi_index: int) NatAccNote

Creates a note in line with this notation

Parameters:
  • pc_symbol – A symbol denoting the pitch class (typically something like ‘C#’, ‘Ab’, ‘F’, etc)

  • nat_bi_index – The base interval index of the natural (for example a B#-0 in 12-EDO has the natural base interval index of 0, even though the pitch is in base interval 1)

note_by_numdef(nat_index: int, acc_sum_vector: Tuple[int, ...]) NatAccNote

Creates a natural/accidental note by its numerical definition. Autogenerates appropriate symbols

Parameters:
  • nat_index – The natural index of the note

  • acc_sum_vector – The (unweighted) accidental sum vector of the note

note_interval(note_a: NatAccNote, note_b: NatAccNote) NatAccNoteInterval

Deprecated since version 0.2.0: Use interval() instead.

Creates a note interval between two notes created by this notation

Raises:

IncompatibleOriginContexts – If one of the notes has a different notation than this one

Parameters:
  • note_a – The source note

  • note_b – The target note

note_scale(notes: List[NatAccNote] | None = None) NatAccNoteScale

Deprecated since version 0.2.0: Use scale() instead.

Creates a note scale from a list of notes

Raises:

IncompatibleOriginContexts – If one of the notes has a different notation than this one

Parameters:

notes – A list of notes created by this notation

parse_pc_symbol(pc_symbol: str) Tuple[str, str, int, Tuple[int, ...]]

Parses a pitch class symbol into its natural class symbol part and its accidental symbol part. Returns a 4-tuple (natc_symbol, acc_symbol, natc_index, acc_sum_vector) with the parsing result.

pc_scale(pc_symbols: List[str] | None = None, root_nat_bi_index: int = 0) NatAccNoteScale

Constructs a note scale from a list of pitch class symbols. The pitch class symbols are assumed to be in the order they appear in the scale meaning that e.g. in 12-EDO the provided argument [‘G’, ‘D’, ‘E’] will result in a scale with notes G0, D1, E1. The base interval of the natural of the first provided pc symbol will always assumed to be 0.

Raises:

UnknownNoteSymbol – If one of the pc symbols is not valid in the definition of this notation

Parameters:
  • pc_symbols – A list of pitch class symbols.

  • root_nat_bi_index – (optional, defaults to 0) The base interval index of the natural of the root (for example a B#-2 in 12-EDO has the natural base interval index of 2, even though the pitch is in base interval 3).

set_interval_symbol_code(nat_diffc: int, symbol_code: SymbolCode)

Sets an interval class symbol for a natural index difference class. The natural index difference class is a number between 0 and the number of naturals in the notation (exclusive), so e.g. [0, …, 6] in a traditional Western system that has 7 naturals. It is calculated by taking the absolute distance of two natural indices modulo the number of naturals in the notation, so e.g. 2 for intervals (C0, E#0), (C0, E1) and (E#2, C0).

The natural index difference class is closely related to the Roman numeral index of intervals, for example, a difference of 2 is the same as III, a difference of 0 is the same as I, etc.

Associating a natural index difference class with a specific symbol code allows setting different interval naming schemes for different interval numbers, e.g. making a difference between perfect and imperfect interval naming schemes. In the Western system differences 0 (unison), 3 (fourth) and 4 (fifth) use the P/A/d interval symbols while for differences 1 (second), 2 (third), 5 (sixth), 6 (sevenths) the system M/m/A/d is used.

Raises:

InvalidNaturalDiffClassIndex – If the natural diff class index is out of bounds

Parameters:
  • nat_diffc – The difference of the natural indices forming the interval modulo the naturals count

  • symbol_code – A symbol code defining how the center and the deviations from it should be represented as strings.

shorthand_interval(symbol: str, number: int) NatAccNoteInterval

Creates an interval without specifying two notes.

A caveat for tunings that are not equal-step: Intervals can have irregular sizes, even if they are notated the same. This method will automatically add some reference note to the interval to be able to calculate ANY size of the interval, so the result of this method might differ in size from a result that you obtained by forming an interval through the note_interval method.

Parameters:
  • symbol – An interval symbol of this notation (for example P, A, M, m)

  • number – An interval number indicating the interval step width according to the convention laid out by this notation

std_pitch_diff(nat_diff: int) PeriodicIndexT

Returns the standardized pitch difference for a natural index difference (e.g. in 12-EDO: 0 -> 0, 1 -> 2, 4 -> 7, (-1) -> (-2), (-4) -> (-7), etc)

Parameters:

nat_diff – A natural index difference

property zero_element: NatAccNote

The ‘standard note’ with pitch_index 0

class xenharmlib.core.notation.NotationABC(tuning, *, note_cls: type[NoteT], note_interval_cls: type[IntervalT], note_scale_cls: type[ScaleT], note_interval_seq_cls: type[IntervalSeqT], note_interval_fan_cls: type[IntervalFanT], note_seq_cls: type[NoteSeqT])

Abstract base class for all notations. A notation can be understood as a wrapper around the tuning, providing a string interface to the underlying integer or lattice point system.

Parameters:

tuning – The tuning for which the notation should be constructed

Additional, optional keyword-only arguments: (only relevant if you want to develop your own notation)

Parameters:
  • note_cls – The python class that is used for the note object returned from the note method.

  • note_interval_cls – The python class that is used for the note interval object returned from the interval builder methods.

  • note_scale_cls – The python class that is used for the note scale object returned from the scale builder methods.

  • note_interval_seq_cls – The python class that is used for the note interval sequence object returned from the interval sequence builder methods.

  • note_interval_fan_cls – The python class that is used for the note interval fan object returned from the interval fan builder methods.

  • note_seq_cls – The python class that is used for the note sequence object returned from the sequence builder methods.

closest_freq_repr(frequency: Frequency) NoteT

Returns the closest note in the notation to a given frequency.

Parameters:

frequency – The frequency in Hz

closest_interval(frequency_ratio: FrequencyRatio) IntervalT

Returns the interval closest to a given frequency ratio

Parameters:

frequency_ratio – The frequency ratio to approximate

closest_interval_fan(frequency_ratios: Iterable[FrequencyRatio]) IntervalFanT

Returns the interval fan closest to a given iterable of frequency ratios.

Parameters:

frequency_ratios – An iterable of frequency ratios

closest_interval_seq(frequency_ratios: Iterable[FrequencyRatio]) IntervalSeqT

Returns the interval sequence closest to a given iterable of frequency ratios.

Parameters:

frequency_ratios – An iterable of frequency ratios

closest_scale(frequencies: Iterable[Frequency]) ScaleT

Returns the scale closest to a given iterable of frequencies

Parameters:

frequencies – An iterable of frequencies given in Hz

closest_seq(frequencies: Iterable[Frequency]) NoteSeqT

Returns the note sequence closest to a given iterable of frequencies

Parameters:

frequencies – An iterable of frequencies given in Hz

diff_interval_fan(pitch_diffs: Iterable[IndexT] | None = None) IntervalFanT

Returns a note interval fan from an iterable of pitch index differences

Parameters:

pitch_diffs – An iterable containing pitch index differences

diff_interval_seq(pitch_diffs: Iterable[IndexT] | None = None) IntervalSeqT

Returns an interval sequence from an iterable of pitch index differences

Parameters:

pitch_diffs – An iterable containing pitch index differences

property enharm_strategy

The enharmonic strategy of this notation. Enharmonic strategies define different ways to map pitch layer objects to notation layer objects.

guess_note(pitch) NoteT

Guesses a note from a pitch using the preferred enharmonic strategy of this notation

Pitch:

A pitch object originating from the underlying tuning

guess_note_interval(pitch_interval) IntervalT

Guesses a note interval from a pitch interval using the preferred enharmonic strategy of this notation

Pitch_interval:

A pitch interval object originating from the underlying tuning

guess_note_interval_fan(pitch_interval_fan) IntervalFanT

Guesses a note interval fan from a pitch interval fan using the preferred enharmonic strategy of this notation

Pitch_interval_fan:

A pitch interval fan object originating from the underlying tuning

guess_note_interval_seq(pitch_interval_seq) IntervalSeqT

Guesses a note interval sequence from a pitch interval sequence using the preferred enharmonic strategy of this notation

Pitch_interval_seq:

A pitch interval sequence object originating from the underlying tuning

guess_note_scale(pitch_scale) ScaleT

Guesses a note scale from a pitch scale using the preferred enharmonic strategy of this notation

Pitch_scale:

A pitch scale object originating from the underlying tuning

guess_note_seq(pitch_seq) NoteSeqT

Guesses a note sequence from a pitch sequence using the preferred enharmonic strategy of this notation

Pitch_seq:

A pitch sequence object originating from the underlying tuning

index_scale(pitch_indices: Iterable[IndexT] | None = None) ScaleT

Constructs a note scale from a list of pitch indices.

Parameters:

pitch_indices – A list of pitch indices

index_seq(pitch_indices: Iterable[IndexT] | None = None) NoteSeqT

Constructs a note sequence from a list of pitch indices.

Parameters:

pitch_indices – A list of pitch indices

abstractmethod note(*args, **kwargs) NoteT

(Must be overwritten by subclasses) Returns a note object of the note type this notation was initialized with

abstractmethod note_interval(note_a: NoteT, note_b: NoteT) IntervalT

Returns a note interval of the note interval type this notation was initialized with

Parameters:
  • note_a – The source note

  • note_b – The target note

abstractmethod note_scale(notes: List[NoteT] | None = None) ScaleT

Returns a note scale of the note scale type this notation was initialized with

Parameters:

notes – A list of notes

property tuning

Returns the tuning this notation was built for

Note and NoteInterval

The note core module implements primitives to handle different types of notes and note intervals. Notes and note intervals wrap the integer-based pitch and pitch interval classes.

class xenharmlib.core.notes.NatAccNote(notation, frequency, pitch_index, nat_index: int, acc_sum_vector: Tuple[int, ...], acc_diff_vector: Tuple[PeriodicIndexT, ...], pc_symbol: str, natc_symbol: str, acc_symbol: str)

A base class for notes that are constructed from a natural and accidentals (the appropriate base class for notes of notations subclassing from NatAccNotation)

Parameters:
  • notation – The notation object this note belongs to

  • frequency – The frequency this note represents

  • pitch_index – The pitch index of this note

  • nat_index – The natural index of this note, which is an index counting the naturals starting with 0 (e.g. in Western notation C0 ^= 0, D0 ^= 1, C1 ^=7, etc)

  • acc_sum_vector – An (unweighted) vector detailing the different deviations that were introduced through accidentals

  • acc_diff_vector – An (weighted) vector detailing the different pitch deviations that were introduced through accidentals

  • pc_symbol – The chosen symbol for the pitch class (in most notations this is equal to natc_symbol + acc_symbol, however, there are notations - like UpDownNotation - that put some of the accidentals before the natural)

  • natc_symbol – The chosen symbol for the natural class (e.g. ‘C’ for C#)

  • acc_symbol – The chosen symbol for the accidental (e.g. ‘#’ for C#)

acc_altered(acc_diff: Tuple[PeriodicIndexT, ...])

Deprecated since version 0.4.0: Use add_acc_diff_vector() instead.

Returns a note with altered accidentals from an accidental difference vector, so for example in UpDownNotation for a tuning with sharpness 2 altering ^C# by (2, -1) results in the note Cx

Parameters:

acc_diff – The accidental difference vector

property acc_diff_vector: Tuple[PeriodicIndexT, ...]

The (weighted) accidental diff vector of this note

property acc_direction: int

The accidental direction of this note (0 if the note is a natural, 1 if the note is a sharp note, -1 if it is a flat note)

property acc_sum_vector: Tuple[int, ...]

The (unweighted) accidental sum vector of this note

property acc_symbol: str

The symbol for the accidental of this note

property acc_value: PeriodicIndexT

The accidental value of this note (e.g. in 31edo 2 for #, -2 for b, 0 for natural)

property acc_vector: Tuple[PeriodicIndexT, ...]

Deprecated since version 0.4.0: Use acc_diff_vector() instead.

The accidental diff vector of this note

add_acc_diff_vector(acc_diff_vector: Tuple[PeriodicIndexT, ...])

Returns a note with altered accidentals by combining the accidental diff vector of this note with a given accidental diff vector.

The accidental diff vector counts the aggregated pitch difference introduced by each set of independent accidentals, for example the pitch difference introduced by all accidentals in the sharp/flat set in the first dimension and the pitch difference introduced by all accidentals in the up/down set in the second dimension

An example in 31-EDO UpDownNotation (where sharps/flats introduce a pitch alteration of 2/-2 respectively)

>>> from xenharmlib import EDOTuning
>>> from xenharmlib import UpDownNotation
>>>
>>> edo31 = EDOTuning(31)
>>> n_edo31 = UpDownNotation(edo31)
>>> C = n_edo31.note('C', 0)
>>> C.add_acc_diff_vector((-2, 1))
UpDownNote(^Cb, 0, 31-EDO)
>>> sharpened = C.add_acc_diff_vector((2, 0))
>>> sharpened
UpDownNote(C#, 0, 31-EDO)
>>> sharpened.pitch_index - C.pitch_index
2
>>> invalid = C.add_acc_diff_vector((1, 0))
Traceback (most recent call last):
...
ValueError: accidental diff vector is not in
the image of the accidental weight mapping
Parameters:

acc_diff_vector – The accidental diff vector to be added

add_acc_sum_vector(acc_sum_vector: Tuple[int, ...])

Returns a note with altered accidentals by combining the accidental sum vector of this note with a given accidental sum vector.

The accidental sum vector counts the aggregated values of each set of independent accidentals, for example the sum of all accidentals in the sharp/flat set in the first dimension and the sum of all accidentals in the up/down set in the second dimension.

An example in UpDownNotation:

>>> from xenharmlib import EDOTuning
>>> from xenharmlib import UpDownNotation
>>>
>>> edo31 = EDOTuning(31)
>>> n_edo31 = UpDownNotation(edo31)
>>> C = n_edo31.note('C', 0)
>>> C.add_acc_sum_vector((-2, 1))
UpDownNote(^Cbb, 0, 31-EDO)
>>> sharpened = C.add_acc_sum_vector((1, 0))
>>> sharpened
UpDownNote(C#, 0, 31-EDO)
>>> sharpened.pitch_index - C.pitch_index
2

Observe that in contrast to the accidental diff vector the sum of the vector components of the parameter does not result in the pitch difference.

Parameters:

acc_sum_vector – The accidental sum vector to be added

property is_enharmonic_natural: bool

Returns True if note refers to a pitch class that is a natural

is_notated_equivalent(other) bool

Returns True, if this note is notated the same way as the other, False otherwise

Parameters:

other – Another note to compare

property is_notated_natural: bool

Returns True if this note is notated(!) as a natural, False otherwise (e.g. the note E# refers to a natural, however, it is notated with an accidental, thus the property will be False here)

is_notated_same(other) bool

Returns True, if this note is notated the same way as the other, False otherwise

Parameters:

other – Another note to compare

property nat_bi_index: int

The base interval index of the natural of this note

property nat_index: int

Returns the natural index of this note. The natural index is the number of steps needed to reach the natural part of this note, so for example in a notation with naturals C, D, E, F, G, A, B the natural index of C#-0 is 0, D-1 is 8, Eb-3 is 16

property nat_pc_index: PeriodicIndexT

The pitch class index of the natural of this note

property nat_pitch_index: PeriodicIndexT

The pitch index of the natural of this note

property natc_index: int

Returns the natural class index of this note. The natural class index is the equivalency class of the natural index, so for example in a notation with naturals C, D, E, F, G, A, B the notes C#-3 and Cb-0 both have natural class index 0 while F#-2 and Fbb-5 have natural class index 3

property natc_pitch_index: PeriodicIndexT

The pitch index of the natural class of this note

property natc_symbol: str

The symbol for the natural of this note

property pc_short_repr

The pitch class symbol of this note

property pc_symbol: str

The pitch class symbol of this note

property pitch: PeriodicPitch

Returns the underlying pitch object

property short_repr

A shortened representation of this note

transpose(diff: PeriodicIndexT | NatAccNoteInterval) NatAccNote

Transposes the note to another one by a natural/accidental note interval.

Parameters:

diff – A natural/accidental note interval object or an integer denoting the pitch difference

transpose_bi_index(bi_diff: int) Self

Returns a note with the same pitch class index and symbol, but with a transposed base interval

Parameters:

bi_diff – The difference in base interval between this note and the resulting one

class xenharmlib.core.notes.NatAccNoteInterval(notation, frequency_ratio, pitch_diff, ref_note: NatAccNoteT, nat_diff: int, acc_sum_vector: Tuple[int, ...], acc_diff_vector: Tuple[PeriodicIndexT, ...], symbol: str, number: int)

Note interval class for intervals with natural/accidental notes. The class assumes that the interval is value-representable by the difference in natural indices and an accidental vector signifying step alterations of different categories. It is meant as a solid basis for interval notations that are similar to the traditional Western interval notation having a interval symbol (like ‘M’) and an interval number (like 2)

The concrete way an interval symbol and number are chosen is dependent on the underlying notation from which a symbol and a number are received in the from_source_and_target() builder method.

Parameters:
  • notation – The notation this interval refers to

  • frequency_ratio – A frequency ratio object

  • pitch_diff – The difference in pitch that this interval represents

  • ref_note – A reference note (needed for non-equal step tunings)

  • nat_diff – The difference of the natural indices of the two notes defining the interval

  • acc_sum_vector – The (unweighted) accidental vector defining the semantic alteration of the standard pitch index difference of a natural index difference

  • acc_diff_vector – The (weighted) accidental vector defining the pitch diff alteration of the standard pitch index difference of a natural index difference

  • symbol – An interval symbol (like ‘M’, ‘d’, ‘P’)

  • number – An interval number

property acc_diff_vector: Tuple[PeriodicIndexT, ...]

The (weighted) accidental diff vector of this interval

property acc_sum_vector: Tuple[int, ...]

The (unweighted) accidental sum vector of this interval

property acc_vector: Tuple[PeriodicIndexT, ...]

Deprecated since version 0.4.0: Use acc_diff_vector() instead.

The accidental vector of this interval (signifying the different pitch deviations from the standard natural pitch difference)

classmethod from_notes(note_a: NatAccNoteT, note_b: NatAccNoteT) Self

Deprecated since version 0.2.0: Use from_source_and_target() instead.

Creates a note interval from two notes

Raises:

IncompatibleOriginContexts – If notes belong to different notations

Parameters:
  • note_a – The source note

  • note_b – The target note

classmethod from_source_and_target(source: NatAccNoteT, target: NatAccNoteT) Self

Creates a note interval from two notes

Raises:

IncompatibleOriginContexts – If notes belong to different notations

Parameters:
  • source – The source note

  • target – The target note

is_notated_same(other) bool

Returns True, if this interval is notated the same way as the other, False otherwise

Parameters:

other – Another interval to compare

property nat_diff: int

The difference of the natural indices of the two notes forming the interval

property number: int

A number signifying the size of the interval (closely related to the nat_diff property but traditionally implemented as 1-based index)

property short_repr: str

A short representation string of the interval (to be shown in collections)

property shorthand_name: Tuple[str, int]

A tuple consisting of the interval symbol and the interval number

property symbol: str

A symbol classifying this interval in regard to size and quality

class xenharmlib.core.notes.NoteABC(notation, frequency, pitch_index)

Abstract base class for notes. Implements the properties tuning, frequency and pitch_index as well as the equality and lesser-than relation based on the frequency property.

Subclasses must implement the pitch property, the is_notated_same method and the transpose() method.

Parameters:
  • notation – The notation object this note belongs to

  • pitch_index – The pitch index of this note

  • frequency – The frequency this note represents

property enharm_strategy

Proxy property for the enharmonic strategy of the notation

abstractmethod is_notated_same(other) bool

(Must be implemented by subclasses) Returns True, if this note is notated the same way as the other, False otherwise

Parameters:

other – Another note of the same notation or class

property notation

The notation associated with this note

abstract property pitch

(Must be implemented by subclasses) Returns the underlying pitch object

property tuning

The tuning associated with this note

class xenharmlib.core.notes.NoteIntervalABC(notation, frequency_ratio, pitch_diff: IndexT, ref_note: NoteT)

Abstract base class for note intervals. Implements the property pitch_interval that constructs the equivalent pitch interval

Note intervals are implemented as generic types with the inner type being a note class.

Subclasses must at least implement the from_source_and_target() class method.

Parameters:
  • notation – The notation this interval refers to

  • frequency_ratio – A frequency ratio object

  • pitch_diff – The difference in pitch that this interval represents

  • ref_note – A reference note (needed for non-equal step tunings)

abstractmethod is_notated_same(other) bool

(Must be implemented by subclasses) Returns True, if this interval is notated the same way as the other, False otherwise

Parameters:

other – Another interval of the same notation

property notation

The notation associated with this note interval

property pitch_interval

Returns the pitch interval equivalent to this note interval

property ref_note: NoteT

A reference note for the interval. (This is important for tunings that are not equal step where the same pitch difference does not imply the same frequency ratio)

property tuning

The tuning associated with this note interval

class xenharmlib.core.notes.PeriodicNoteABC(notation, frequency, pitch_index)

Abstract base class for periodic notes. Implements proxy properties pc_index and bi_index that refer to the underlying periodic pitch object.

Subclasses need to implement the transpose_bi_index() method (in addition to the abstract properties and methods of NoteABC)

property bi_index: int

The base interval index of this note

is_equivalent(other: PeriodicPitchLike) bool

Returns True if this note has the same frequency as the other object when normalized to the first base interval

Parameters:

other – Another periodic pitch or note

abstractmethod is_notated_equivalent(other) bool

(Must be implemented by subclasses) Returns True, if this note is notated the same way as the other in regards to its pitch class symbol

Parameters:

other – Another note

property pc_index: PeriodicIndexT

The pitch class index of this note

pcs_normalized() Self

Returns the equivalent of this note in the first base interval

abstractmethod transpose_bi_index(bi_diff: int) Self

Returns a note with the same pitch class index and symbol, but with a transposed base interval

Parameters:

bi_diff – The difference in base interval between this note and the resulting one

class xenharmlib.core.notes.PeriodicNoteInterval(notation, frequency_ratio, pitch_diff: IndexT, ref_note: NoteT)

Abstract base class for intervals referring to notations of periodic tunings.

property ic_index: PeriodicIndexT

Returns the interval class index of this interval (often simply called “interval class” or “ic”) as known from pitch class set theory.

The interval class is the shortest distance in pitch class space between two unordered pitch classes.

It is calculated by comparing the absolute value of the simple portion of the interval and its inversion, returning whatever pitch difference is smaller, so e.g. in 12-EDO the ic class for P5 is min(7, 5) = 5

ic_normalized() Self

Returns an interval class normalized version of this interval. The interval normalized version is calculated by converting (if necessary) this interval into a simple interval, normalizing it to a upwards interval and then building the minimum from that result and its inversion.

inversion() Self

Returns the inversion of this interval. The inversion is calculated by subtracting this interval from the equivalency interval.

property is_compound: bool

Returns True if this interval is a compound interval, False otherwise. A compound interval is defined as an interval whose absolute value is strictly greater than the equivalency interval (meaning the equivalency interval itself and its negation are not considered compound)

property is_simple: bool

Returns True if this interval is a simple interval, False otherwise. A simple interval is defined as an interval whose absolute value is lesser or equal than the equivalency interval (meaning the equivalency interval itself and its negation are considered a simple interval)

to_simple() Self

Returns the corresponding simple interval if this is a compound interval (or the interval itself if it is already simple)

The method preserves direction, so if this interval is a downward interval, the resulting interval will also be a downward interval.

NoteScale

The note scale core module implements basic classes for different types of scale notation systems.

class xenharmlib.core.note_scale.NatAccNoteScale(notation, notes=None)

Basic note scale class for natural/accidental notations. Implements the scale equivalents of properties special to natural/accidental notes.

property acc_diff_vectors: List[Tuple[PeriodicIndexT]]

A list of (weighted) accidental diff vectors for each note in the scale

property acc_directions: List[int]

The list of accidental directions of notes in the scale (0 if the note is a natural, 1 if the note is a sharp note, -1 if it is a flat note, so for example in 31-EDO [0, 1, -1] for [C0, ^B#2, Cb0])

property acc_sum_vectors: List[Tuple[int]]

A list of (unweighted) accidental sum vectors for each note in the scale

property acc_symbols: List[str]

The symbol list for the accidental part of each note in the scale (e.g. in 12-EDO [‘#’, ‘b’, ‘’] for [C#0, Gb1, B4])

property acc_values: List[PeriodicIndexT]

A list of accidental values for each note in the scale

property acc_vectors: List[Tuple[PeriodicIndexT]]

Deprecated since version 0.4.0: Use acc_diff_vectors() instead.

A list of accidental vectors for each note in the scale

property nat_bi_indices: List[int]

A list of natural base interval indices represented in this scale. The natural base interval is the base interval index of the natural part of the note, so e.g. 0 for B#-0

property nat_indices: List[int]

A list of the natural indices of notes in this scale

The natural index is the number of natural steps needed to reach the natural part of this note, so for example in a notation with naturals C, D, E, F, G, A, B the natural index of C#-0 is 0, D-1 is 8, Eb-3 is 16

property nat_pc_indices: List[PeriodicIndexT]

A list of pitch class indices of the natural part of each note in the scale (e.g. in 12-EDO [0, 2, 4] for [C#0, D1, Eb2])

property nat_pitch_indices: List[PeriodicIndexT]

A list of pitch indices of the natural part of each note in the scale (e.g. in 12-EDO [0, 14, 18] for [C#0, D1, Eb2])

property natc_indices: List[int]

A list of natural class indices of notes in this scale.

The natural class index is the equivalency class of the natural index, so for example in a notation with naturals C, D, E, F, G, A, B the notes C#-3 and Cb-0 both have natural class index 0 while F#-2 and Fbb-5 have natural class index 3

property natc_symbols: List[str]

The symbol list for the natural part of each note in the scale (e.g. in 12-EDO [‘C’, ‘G’, ‘B’] for [C#0, Gb1, B4])

property pc_symbols: List[str]

The symbol list for the pitch classes represented in the scale (e.g. in 12-EDO [‘C#’, ‘Gb’, ‘B’] for [C#0, Gb1, B4])

class xenharmlib.core.note_scale.NoteScale(notation, notes=None)

Base class for all note scales. Implements list and set operations, transposition, etc

Note scales are implemented as generic types with the inner type being a note class.

Parameters:
  • notation – The notation this scale refers to

  • notes – (optional) A list of notes from the notation. If the parameter is omitted an empty scale will be initialized

add_note(note: NoteT)

Deprecated since version 0.2.0: objects in xenharmlib are supposed to be immutable

Inserts a new note into to the scale at the right position. If the note already exists in the scale the method will do nothing.

Raises:

IncompatibleOriginContexts – If the note has a different notation than this scale.

Parameters:

note – The new note

property enharm_strategy

A proxy property to the enharmonic strategy of the notation

is_notated_disjoint(other: Self) bool

Determines if this scale has any common notes with another scale of the same notation. In contrast to is_disjoint enharmonically equivalent but differently notated notes will be treated as distinct

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

is_notated_same(other: Self) bool

Returns True if this scale has the exact same notes as the other scale while ignoring possible enharmonic equivalence.

is_note_subset(other: Self, proper: bool = False) bool

Determines if all notes in this scale also exist in the other scale. In contrast to is_subset enharmonically equivalent but differently notated notes will be treated as distinct

Parameters:
  • other – Another scale of the same notation

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

is_note_superset(other: Self, proper: bool = False) bool

Determines if all notes in the other scale also exist in this scale. In contrast to is_superset enharmonically equivalent but differently notated notes will be treated as distinct

Parameters:
  • other – Another scale of the same notation

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

property is_zero_normalized: bool

Returns True if this function is zero normalized, meaning that the first element of the scale is notated the same as the zero element of the origin context (typically C0 in western-like notations)

note_difference(other: Self) Self

Returns a new scale containing only notes from this scale that are NOT present in the other scale. In contrast to the difference method notes will only be considered shared notes of the sets if they are notated the same. If a note is in the second set that is enharmonically equivalent to a note in this set but notated in a different way, the latter will stay in the result set.

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

note_intersection(other: Self) Self

Returns a new scale including all notes that are included in both scales. In contrast to the intersection method notes will only be considered shared notes of the sets if they are notated the same and excluded otherwise, even if they are enharmonically equivalent

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

property pitch_indices: List[IndexT]

An ordered list of pitch indices present in this scale

property pitch_scale

Returns the equivalent pitch scale for this object

to_note_intervals() List[NoteIntervalABC]

Deprecated since version 0.2.0: Use to_interval_seq() instead.

Returns this scale represented as a list of note intervals

transpose(diff: IndexT | NoteIntervalABC) Self

Transposes the scale by the given interval

Parameters:

diff – A note interval or pitch difference

property tuning

The tuning the origin notation of this scale relies upon

class xenharmlib.core.note_scale.PeriodicNoteScale(notation, notes=None)

Note scale class for periodic notations. Implements customized set operations (for when you want to treat equivalent notes the same as equal notes).

is_notated_disjoint(other: Self, ignore_bi_index: bool = False) bool

Determines if this scale has any common notes with another scale of the same notation. In contrast to is_disjoint enharmonically equivalent but differently notated notes will be treated as distinct

Parameters:
  • other – Another scale of the same notation

  • ignore_bi_index – (Optional, default False) When set to True notes of the same pitch class will be treated the same. For example, if one scale includes C-0 and another C-1 the scales will not be considered disjoint

Raises:

IncompatibleOriginContexts – If the other scale originates from a different notation

is_notated_equivalent(other: Self) bool

Returns True if this scale has, apart from the base interval, the exact same notes as the other while ignoring possible enharmonic equivalence.

is_note_subset(other: Self, proper: bool = False, ignore_bi_index: bool = False) bool

Determines if all notes in this scale also exist in the other scale. In contrast to is_subset enharmonically equivalent but differently notated notes will be treated as distinct

Parameters:
  • other – Another scale of the same notation

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

  • ignore_bi_index – (Optional, default False) When set to True notes of the same pitch class will be treated the same.

Raises:

IncompatibleOriginContexts – If the other scale originates from a different notation

is_note_superset(other: Self, proper: bool = False, ignore_bi_index: bool = False) bool

Determines if all notes in the other scale also exist in this scale. In contrast to is_superset enharmonically equivalent but differently notated notes will be treated as distinct

Parameters:
  • other – Another scale of the same notation

  • proper – (Optional, default False) When set to True method will return False if the two sets are identical

  • ignore_bi_index – (Optional, default False) When set to True notes of the same pitch class will be treated the same.

Raises:

IncompatibleOriginContexts – If the other scale originates from a different notation

note_difference(other: Self, ignore_bi_index: bool = False) Self

Returns a new scale containing only notes from this scale that are NOT present in the other scale. In contrast to the difference method notes will only be considered shared notes of the sets if they are notated the same. If a note is in the second set that is enharmonically equivalent to a note in this set, but notated in a different way, the latter will stay in the result set.

Parameters:
  • other – Another scale of the same notation

  • ignore_bi_index – (Optional, default False) When set to True notes of the same pitch class will be treated the same. For example, if the difference of two scales including C-0 and C-1 respectively is calculated, C-0 will not be inserted into the new scale

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

note_intersection(other: Self, ignore_bi_index: bool = False) Self

Returns a new scale including all notes that are included in both scales. In contrast to the intersection method notes will only be considered shared notes of the sets if they are notated the same and excluded otherwise, even if they are enharmonically equivalent

Parameters:

ignore_bi_index – (Optional, default False) When set to True notes of the same pitch class will be treated the same. For example, if the intersection of two scales including C-0 and C-1 respectively is calculated, both notes will be added to the result

Raises:

IncompatibleOriginContexts – If the other scale has a different notation

property pc_indices: List[PeriodicIndexT]

Returns a list of pitch class indices in the order they appear in this scale. This can include duplicate items if the scale has two notes of the same pitch class

pcs_complement() Self

Returns a complement version of this scale that includes all the notes in the first base interval that are not part of this scale

Enharmonic Strategies

This module includes functionality to deal with enharmonic ambiguity

class xenharmlib.core.enharm_strategies.EnharmonicStrategy

An enharmonic strategy is a drop-in function set to solve the problem of enharmonic ambiguity when translating pitches into notes.

An example: On the pitch/tuning layer the transpose method is well-defined if given an integer:

>>> from xenharmlib import EDOTuning
>>> edo12 = EDOTuning(12)
>>> edo12.pitch(0).transpose(1)
EDOPitch(1, 12-EDO)

However on the notation layer it is unclear, if “C + 1” should result in C# or Db, if “C + 2” should result in “D” or “C##”, etc. The same problem is encountered on operations like pcs_complement: Should the PCS complement of [C, D, E, F, G, A, B] be notated with upwards accidentals [C#, D#, F#, G#, A#], downward accidentals [Db, Eb, Gb, Ab, Bb] or a mixture of both?

An enharmonic strategy makes an opinionated(!) choice which pitches translate to which notes. Because not every strategy is fit for every context and purpose xenharmlib allows choosing a custom strategy when initializing a notation.

This base class implements stubs of all hook functions which get called by the notation layer classes. By default these stubs are not abstract, but simply raise NotImplementedError, so it is possible for EnharmonicStrategy subclasses to not support all operations that need an enharmonic strategy. This way custom enharmonic strategies implemented by the user also keep working if the feature set expands on an update.

guess_note(notation, pitch)

Notation.guess_note relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch – The pitch object

guess_note_interval(notation, pitch_interval)

Notation.guess_note_interval relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_interval – The pitch interval object

guess_note_interval_fan(notation, pitch_interval_fan)

Notation.guess_note_interval_fan relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_interval_fan – The pitch interval fan object

guess_note_interval_seq(notation, pitch_interval_seq)

Notation.guess_note_interval_seq relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_interval_seq – The pitch interval sequence object

guess_note_scale(notation, pitch_scale)

Notation.guess_note_scale relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_scale – The pitch scale object

guess_note_seq(notation, pitch_seq)

Notation.guess_note_seq relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_seq – The pitch sequence object

note_scale_pcs_complement(note_scale)

NoteScale.pcs_complement relays to here

Parameters:

note_scale – The note scale on which the pcs_complement method was called

note_scale_transpose(note_scale, pitch_diff)

NoteScale.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as a pitch diff.

Parameters:
  • note_scale – The note scale on which the transpose method was called

  • pitch_diff – The pitch difference

note_seq_transpose(note_seq, pitch_diff)

NoteSeq.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as a pitch diff.

Parameters:
  • note_seq – The note seq on which the transpose method was called

  • pitch_diff – The pitch difference

note_transpose(note, pitch_diff)

Note.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as a pitch diff.

Parameters:
  • note – The note on which the transpose method was called

  • pitch_diff – The pitch difference

class xenharmlib.core.enharm_strategies.PCBlueprintStrategy(pc_blueprint: Sequence[PeriodicNoteABC])

PCBlueprintStrategy is an enharmonic strategy that defines fixed note symbols for each pitch class and translates every pitch to a note by matching the pitch class and transposing the base interval.

Parameters:

pc_blueprint – An iterable of notes that together form a pitch class blueprint for the whole pitch range

>>> from xenharmlib import EDOTuning
>>> from xenharmlib import UpDownNotation
>>> from xenharmlib.core.enharm_strategies import PCBlueprintStrategy
>>>
>>> edo12 = EDOTuning(12)
>>> n_edo12 = UpDownNotation(edo12)
>>>
>>> S = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
>>> strategy = PCBlueprintStrategy(
...     [n_edo12.note(symbol, 0) for symbol in S]
... )
>>> n_edo12.enharmonic_strategy = strategy
>>>
>>> n_edo12.guess_note(edo12.pitch(13))
UpDownNote(C#, 1, 12-EDO)
>>> n_edo12.note('D#', 2).transpose(1)
UpDownNote(E, 2, 12-EDO)
guess_note(notation, pitch)

Notation.guess_note relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch – The pitch object

guess_note_interval(notation, pitch_interval)

Notation.guess_note_interval relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch – The pitch interval object

guess_note_interval_fan(notation, pitch_interval_fan)

Notation.guess_note_interval_fan relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_interval_fan – The pitch interval fan object

guess_note_interval_seq(notation, pitch_interval_seq)

Notation.guess_note_interval_seq relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_interval_seq – The pitch interval sequence object

guess_note_scale(notation, pitch_scale)

Notation.guess_note_scale relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_scale – The pitch scale object

guess_note_seq(notation, pitch_seq)

Notation.guess_note_seq relays to here

Parameters:
  • notation – The notation object that relayed here

  • pitch_seq – The pitch sequence object

note_scale_pcs_complement(note_scale)

NoteScale.pcs_complement relays to here

Parameters:

note_scale – The note scale on which the pcs_complement method was called

note_scale_transpose(note_scale, pitch_diff: int)

NoteScale.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as an integer.

Parameters:
  • note_scale – The note scale on which the transpose method was called

  • pitch_diff – The pitch difference as an integer

note_seq_transpose(note_seq, pitch_diff: int)

NoteScale.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as an integer.

Parameters:
  • note_seq – The note seq on which the transpose method was called

  • pitch_diff – The pitch difference as an integer

note_transpose(note, pitch_diff: int)

Note.transpose relays to here if the interval argument was not given as a suitable NoteInterval object but as an integer.

Parameters:
  • note – The note on which the transpose method was called

  • pitch_diff – The pitch difference as an integer

Symbol Codes

The symbol module implements primitives to parse languages in which each literal and each word represents an integer vector. These languages are called ‘symbol codes’ and are used as utils in notations.

exception xenharmlib.core.symbols.AmbiguousSymbol

Gets raised whenever a symbol is added to a symbol code that already exists or if the associated value is already represented by another symbol

class xenharmlib.core.symbols.SymbolArithmetic(dimensions: int = 1, offset: Tuple[int, ...] | None = None, allow_empty: bool = False)

A symbol arithmetic is a mapping between string symbol sequences and integer vector sums.

A simple use case is to parse and generate accidentals, for example parsing the standard western accidental string ‘x#b’ into a list of one-dimensional integer vectors that signify the pitch index alterations of each accidental (like (2,), (1,), (-1,)), resulting in the sum (2,) denoting the total pitch index alteration of an accidental string.

Higher dimensional arithmetics can be useful if there are multiple classes of accidentals (like sharp/flat on the one hand and up/down on the other). Given a value dimensionality of 2, it can e.g. parse an expression like ‘^b##x’ into a list of integer vectors (here: (0, 1), (-1, 0), (1, 0), (1, 0), (2, 0)) from which it creates a sum vector (3, 1)

Vice-versa for a given integer or integer vector it can create an equivalent minimal sequence of symbols (e.g. (-1, 1) -> ‘b^’).

SymbolArithmetic parses in a greedy way, so if ‘bb’, ‘b’ and ‘#’ are registered as symbols, it will parse ‘bb#’ into the list ‘bb’, ‘#’ (NOT ‘b’, ‘b’, ‘#’)

The class can also be used to design accidental systems for dense tunings (like prime limit tunings), that don’t have an enumerable pitch index sequence. For example, given a 3-limit tuning one can define accidentals as their monzo, defining ‘#’ as the value vector (-11, 7), ‘b’ as the vector (11, -7), and so forth. The addition of the monzo vectors is then equal to the product in |R+: (e.g. ‘b#’ = (11, -7) + (-11, 7) = (0, 0), which is the same as (2^11)/(3^7) * (3^7)/(2^11) = (2^0) * (3^0) = 1.

On initialization of an arithmetic, an offset can be set, which adds a fixed integer vector to all symbol value vectors. This is especially powerful when defining partial arithmetics in a SymbolArithmeticSet, e.g. if one wants to parse ‘^A’ into (4, 1), ‘AA’ into (5, 0), ‘^AAA’ into (6, 1), etc. In a case like this one can define the value of ‘A’ to be (1, 0), the value of ‘^’ to be (0, 1) and the offset to be (3, 0).

Parameters:
  • dimensions – Dimensions of the value vector (optional, defaults to 1)

  • offset – (optional, defaults to the 0-vector). A fixed value vector that will be added to the sum

  • allow_empty – (optional, default False). If True, empty strings are part of this arithmetic (with the value of the offset, or the 0 vector if offset is not given). If False exceptions will be raised on empty strings

add_symbol(symbol: str, vector: Tuple[int, ...], position: int | None = None, min_occurence: int | None = None, max_occurence: int | None = None)

Adds a string symbol with its corresponding integer vector to this arithmetic

Raises:

AmbiguousSymbol – If symbol already exists in the arithmetic or if vector is already represented by another symbol

Parameters:
  • symbol – A string (can be multi-character)

  • vector – An integer tuple defining the value vector of the symbol (dimensions must match the dimensions with which the arithmetic was initialized)

  • position – The desired positional value of the symbol in the sorting process of get_symbol_str (optional, if no parameter is given the position of the symbol will be analogous to the order in which symbols were added to the arithmetic)

  • min_occurence – (optional) The minimum number of times this symbol must occur in the arithmetic symbol string in order for the string to be considered valid

  • max_occurence – (optional) The maximum number of times this symbol can occur in the arithmetic symbol string in order for the string to be considered valid

get_symbol_str(vector: Tuple[int, ...]) str

Partitions the given vector into vector summands that are represented by symbols and returns those symbols sorted according to the positional value of each symbol and joined as a single string.

Parameters:

vector – The sum vector that should be resolved into a symbol term

get_symbols(vector: Tuple[int, ...]) Tuple[str, ...]

Returns a sorted, minimal tuple of symbols whose combined value together with the offset result in the given sum.

Raises:

SymbolValueNotMapped – If the value can not be represented by any combination of symbols in the arithmetic

Parameters:

vector – The sum vector that should be resolved into a symbol term

get_vector(symbol_str: str) Tuple[int, ...]

Returns the vector integer sum (adjusted for offset, if set) for a given symbol string.

Raises:

UnknownSymbolString – If arithmetic did not match the string

Parameters:

symbol_str – A string consisting of symbols defined in this arithmetic

get_vector_from_symbols(symbols: Tuple[str, ...]) Tuple[int, ...]

Returns the vector integer sum (adjusted for offset, if set) for a given tuple of parsed symbol literals

Parameters:

symbols – A tuple with each element being a symbol literal in this arithmetic

parse(symbol_str: str) Tuple[str, ...]

Parses a symbol string into a list of symbols

>>> from xenharmlib.core.symbols import SymbolArithmetic
>>> arithmetic = SymbolArithmetic(dimensions=2)
>>> arithmetic.add_symbol('x', (2, 0))
>>> arithmetic.add_symbol('#', (1, 0))
>>> arithmetic.add_symbol('b', (-1, 0))
>>> arithmetic.add_symbol('^', (0, 1))
>>> symbols = arithmetic.parse('xbb#')
>>> symbols
('x', 'b', 'b', '#')
Raises:

UnknownSymbolString – If arithmetic did not match the string

Parameters:

symbol_str – A symbol string consisting of symbols defined in this arithmetic

class xenharmlib.core.symbols.SymbolArithmeticSet(dimensions: int = 1, pref_func: Callable[[List[Tuple[SymbolArithmetic, Tuple[str, ...]]]], Tuple[SymbolArithmetic, Tuple[str, ...]]] | None = None)

SymbolArithmeticSets combine different SymbolArithmetics allowing to use multiple symbols for the same integer vector and to segment the space of integer vectors into multiple arithmetics with different offsets.

You can for example combine four arithmetics to represent traditional interval naming of imperfect intervals:

  • ‘M’ represents vector (0, 0)

  • ‘^M’ represents vector (0, 1)

  • ‘A’ represents vector (2, 0)

  • ‘m’ represents vector (-1, 0)

  • ‘d’ represents vector (-2, 0)

  • ‘vd’ represents vector (-2, -1)

Parameters:
  • dimensions – The dimensions of the arithmetics in the set (optional, defaults to 1)

  • pref_func – (optional) A preference function that returns a definite parsing result from a list of possible ones. The function should accept a list of tuples (arithmetic, parsed_str) with parsed_str being a tuple of single symbols. It should return one element of the list that should be preferred. If no preference function is given, then the class will choose the result with the shortest length.

add_arithmetic(arithmetic: SymbolArithmetic)

Adds another symbol arithmetic to this set

Parameters:

arithmetic – The arithmetic to add

property dimensions: int

The vector dimensions of arithmetics in this set

get_symbol_str(vector: Tuple[int, ...]) str

Returns a minimal symbol string for a given value

Raises:

SymbolValueNotMapped – If the value can not be represented by any combination of symbols in any arithmetic

Parameters:

value – A positive or negative integer

get_vector(symbol_str: str) Tuple[int, ...]

Returns the vector sum value for a given string

Raises:

UnknownSymbolString – If no arithmetic in the set matched the string

Parameters:

symbol_str – A symbol string consisting of symbols defined by at least one arithmetic in the set.

parse(symbol_str: str) Tuple[SymbolArithmetic, Tuple[str, ...]]

Tries to parse a symbol string by each arithmetic in the set. If an arithmetic returns a result it is added to the list of possible results from which then subsequently one result is selected using the preference function given during set initialization.

The function returns a tuple (arithmetic, symbols) with the first element being the chosen arithmetic and the second being the parsing result from that arithmetic.

Raises:

UnknownSymbolString – If no arithmetic in the set matched the string

class xenharmlib.core.symbols.SymbolCode

SymbolCode defines a general interface for different strategies to turn symbol strings into integer vectors and vice versa.

The interface consists of two abstract methods
abstractmethod get_symbol_str(vector: Tuple[int, ...]) str

Abstract method placeholder for a specific implementation to convert an integer vector into a symbol or sequence of symbols

Parameters:

vector – An integer vector

Raises:

SymbolValueNotMapped – If mapping has no ruleset to convert the vector into a string

abstractmethod get_vector(symbol_str: str) Tuple[int, ...]

Abstract method placeholder for a specific implementation to convert a string of one or more symbols into an integer vector

Parameters:

string – A string consisting of one or more symbols

Raises:

UnknownSymbolString – If mapping has no ruleset to convert the string into an integer vector

exception xenharmlib.core.symbols.SymbolValueNotMapped

Gets raised whenever an integer value is not mapped by a word in a symbol code

exception xenharmlib.core.symbols.UnfittingDimensions

Gets raised whenever a vector does not fit to the dimension configuration of the symbol code

exception xenharmlib.core.symbols.UnknownSymbolString

Gets raised whenever a SymbolCode receives a string that is not part of its grammar

Exceptions

This module includes all exceptions that must be handled by the user of the library

exception xenharmlib.exc.IncompatibleOriginContexts

Gets raised whenever two or more objects are not compatible because they are based on different orgin contexts

exception xenharmlib.exc.InvalidAccidentalValue

Gets raised when an accidental value is not allowed in a notation

exception xenharmlib.exc.InvalidBaseIntervalIndex

Gets raised when a base interval index does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.InvalidFrequency

Gets raised when a frequency argument does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.InvalidGenerator

Gets raised when a given pitch or pitch-like argument is not a generator in respect to the tuning

exception xenharmlib.exc.InvalidIndexMask

Gets raised when an index mask expression is invalid

exception xenharmlib.exc.InvalidIntervalNumber

Gets raised when an interval number is not valid

exception xenharmlib.exc.InvalidNaturalDiffClassIndex

Gets raised when a natural diff class index does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.InvalidNaturalIndex

Gets raised when a natural index does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.InvalidPitchClassIndex

Gets raised when a pitch class index does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.InvalidPitchIndex

Gets raised when a pitch index does not adhere to a certain restriction, for example if it is out of bounds of a predefined limit

exception xenharmlib.exc.UnfittingNotation

Gets raised on construction of notations when a property of the tuning prohibits the use of the specific notation

exception xenharmlib.exc.UnknownNoteSymbol

Gets raised on construction of notes if a provided symbol was not recognized by the notation