Skip to content

Interval

interval

Interval parsing and arithmetic.

The Interval namespace handles musical intervals: their names, qualities, arithmetic, and inversions. Intervals are parsed from strings in either "tonal" form (number first: "5P", "-3m") or "shorthand" form (quality first: "P5", "M3").

The two canonical-quality systems:

  • Perfectable intervals (1, 4, 5, 8, 11, 12): qualities P, A, d, AA, dd...
  • Majorable intervals (2, 3, 6, 7, 9, 10, 13, 14): qualities M, m, A, d, AA, dd...

So Interval.get("3P") returns an empty interval — 3rds aren't perfectable.

Example

from tonal_py import Interval Interval.semitones("P5") 7 Interval.invert("3m") '6M' Interval.add("3m", "5P") '7m'

Source parity: @tonaljs/interval

names

names() -> list[str]

Return the seven natural intervals from a major scale's tonic.

Returns:

Type Description
list[str]

["1P", "2M", "3M", "4P", "5P", "6m", "7m"].

Example

from tonal_py import Interval Interval.names() ['1P', '2M', '3M', '4P', '5P', '6m', '7m']

Source code in src/tonal_py/interval.py
def names() -> list[str]:
    """Return the seven natural intervals from a major scale's tonic.

    Returns:
        `["1P", "2M", "3M", "4P", "5P", "6m", "7m"]`.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.names()
        ['1P', '2M', '3M', '4P', '5P', '6m', '7m']
    """
    return ["1P", "2M", "3M", "4P", "5P", "6m", "7m"]

name

name(s: str) -> str

Get the canonical name of an interval.

Both "P4" and "4P" parse the same; the canonical form puts the number first.

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
str

The canonical interval name, or "" if invalid.

Example

from tonal_py import Interval Interval.name("4P") '4P' Interval.name("P4") '4P' Interval.name("C4") ''

Source code in src/tonal_py/interval.py
def name(s: str) -> str:
    """Get the canonical name of an interval.

    Both `"P4"` and `"4P"` parse the same; the canonical form puts the
    number first.

    Args:
        s: An interval string.

    Returns:
        The canonical interval name, or `""` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.name("4P")
        '4P'
        >>> Interval.name("P4")
        '4P'
        >>> Interval.name("C4")
        ''
    """
    return get(s).name

semitones

semitones(s: str) -> float

Get the size of an interval in semitones.

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
float

The size in semitones (negative for descending intervals).

float

math.nan if invalid.

Example

from tonal_py import Interval Interval.semitones("P4") 5 Interval.semitones("M3") 4 Interval.semitones("-2m") -1

Source code in src/tonal_py/interval.py
def semitones(s: str) -> float:
    """Get the size of an interval in semitones.

    Args:
        s: An interval string.

    Returns:
        The size in semitones (negative for descending intervals).
        `math.nan` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.semitones("P4")
        5
        >>> Interval.semitones("M3")
        4
        >>> Interval.semitones("-2m")
        -1
    """
    return get(s).semitones

quality

quality(s: str) -> str

Get the quality string of an interval (P, M, m, A, d, AA, dd, ...).

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
str

The quality character(s), or "" if invalid.

Example

from tonal_py import Interval Interval.quality("P4") 'P' Interval.quality("M3") 'M' Interval.quality("AAA4") 'AA'

Source code in src/tonal_py/interval.py
def quality(s: str) -> str:
    """Get the quality string of an interval (P, M, m, A, d, AA, dd, ...).

    Args:
        s: An interval string.

    Returns:
        The quality character(s), or `""` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.quality("P4")
        'P'
        >>> Interval.quality("M3")
        'M'
        >>> Interval.quality("AAA4")
        'AA'
    """
    return get(s).q

num

num(s: str) -> float

Get the interval number (1-15+, negative for descending).

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
float

The interval number, or math.nan if invalid.

Example

from tonal_py import Interval Interval.num("P4") 4 Interval.num("9M") 9 Interval.num("-3m") -3

Source code in src/tonal_py/interval.py
def num(s: str) -> float:
    """Get the interval number (1-15+, negative for descending).

    Args:
        s: An interval string.

    Returns:
        The interval number, or `math.nan` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.num("P4")
        4
        >>> Interval.num("9M")
        9
        >>> Interval.num("-3m")
        -3
    """
    return get(s).num

simplify

simplify(s: str) -> str

Reduce a compound interval to a simple one within an octave.

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
str

The simple form (e.g. "9M" -> "2M"), or "" if invalid.

Example

from tonal_py import Interval Interval.simplify("9M") '2M' Interval.simplify("2M") '2M' Interval.simplify("-2M") '-2M' Interval.simplify("-9M") '-2M' [Interval.simplify(s) for s in ["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"]]['8P', '2M', '3M', '4P', '5P', '6M', '7M', '1P']

Source code in src/tonal_py/interval.py
def simplify(s: str) -> str:
    """Reduce a compound interval to a simple one within an octave.

    Args:
        s: An interval string.

    Returns:
        The simple form (e.g. `"9M"` -> `"2M"`), or `""` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.simplify("9M")
        '2M'
        >>> Interval.simplify("2M")
        '2M'
        >>> Interval.simplify("-2M")
        '-2M'
        >>> Interval.simplify("-9M")
        '-2M'
        >>> [Interval.simplify(s) for s in ["8P", "9M", "10M", "11P", "12P", "13M", "14M", "15P"]]
        ['8P', '2M', '3M', '4P', '5P', '6M', '7M', '1P']
    """
    i = get(s)
    if i.empty:
        return ""
    return f"{int(i.simple)}{i.q}"

invert

invert(s: str) -> str

Invert an interval (3m ↔ 6M, 2M ↔ 7m, P5 ↔ P4, etc.).

Inversion sums to a perfect octave. The quality also flips: major becomes minor and vice versa; perfect stays perfect; augmented and diminished swap.

Parameters:

Name Type Description Default
s str

An interval string.

required

Returns:

Type Description
str

The inverted interval, or "" if invalid.

Example

from tonal_py import Interval Interval.invert("3m") '6M' Interval.invert("2M") '7m' Interval.invert("5P") '4P'

Source code in src/tonal_py/interval.py
def invert(s: str) -> str:
    """Invert an interval (3m ↔ 6M, 2M ↔ 7m, P5 ↔ P4, etc.).

    Inversion sums to a perfect octave. The quality also flips: major
    becomes minor and vice versa; perfect stays perfect; augmented and
    diminished swap.

    Args:
        s: An interval string.

    Returns:
        The inverted interval, or `""` if invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.invert("3m")
        '6M'
        >>> Interval.invert("2M")
        '7m'
        >>> Interval.invert("5P")
        '4P'
    """
    i = get(s)
    if i.empty:
        return ""
    step = (7 - int(i.step)) % 7
    alt = -int(i.alt) if i.type == "perfectable" else -(int(i.alt) + 1)
    return get(Pitch(step=step, alt=alt, oct=int(i.oct), dir=int(i.dir))).name

from_semitones

from_semitones(semitones_val: int) -> str

Pick a canonical interval name for a given semitone count.

Multiple interval names map to the same number of semitones (e.g. augmented 4th and diminished 5th are both 6 semitones). This function returns one canonical choice — useful but not necessarily what you expect for a given musical context.

Parameters:

Name Type Description Default
semitones_val int

Number of semitones (negative for descending).

required

Returns:

Type Description
str

An interval name with that semitone count.

Example

from tonal_py import Interval Interval.from_semitones(7) '5P' Interval.from_semitones(-7) '-5P' Interval.from_semitones(12) '8P'

Source code in src/tonal_py/interval.py
def from_semitones(semitones_val: int) -> str:
    """Pick a canonical interval name for a given semitone count.

    Multiple interval names map to the same number of semitones (e.g.
    augmented 4th and diminished 5th are both 6 semitones). This function
    returns one canonical choice — useful but not necessarily what you
    expect for a given musical context.

    Args:
        semitones_val: Number of semitones (negative for descending).

    Returns:
        An interval name with that semitone count.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.from_semitones(7)
        '5P'
        >>> Interval.from_semitones(-7)
        '-5P'
        >>> Interval.from_semitones(12)
        '8P'
    """
    d = -1 if semitones_val < 0 else 1
    n = abs(semitones_val)
    c = n % 12
    o = floor(n / 12)
    return f"{d * (_IN[c] + 7 * o)}{_IQ[c]}"

add_to

add_to(interval: Any) -> Callable[[Any], str | None]

Curry [add][tonal_py.interval.add] for use with map.

Parameters:

Name Type Description Default
interval Any

The fixed addend interval.

required

Returns:

Type Description
Callable[[Any], str | None]

A function that adds interval to its argument.

Example

from tonal_py import Interval add_5p = Interval.add_to("5P") [add_5p(i) for i in ["1P", "2M", "3M"]]['5P', '6M', '7M']

Source code in src/tonal_py/interval.py
def add_to(interval: Any) -> Callable[[Any], str | None]:
    """Curry [`add`][tonal_py.interval.add] for use with `map`.

    Args:
        interval: The fixed addend interval.

    Returns:
        A function that adds `interval` to its argument.

    Example:
        >>> from tonal_py import Interval
        >>> add_5p = Interval.add_to("5P")
        >>> [add_5p(i) for i in ["1P", "2M", "3M"]]
        ['5P', '6M', '7M']
    """
    return lambda other: add(interval, other)

transpose_fifths

transpose_fifths(interval_name: str, fifths: int) -> str

Shift an interval by N perfect fifths on the circle of fifths.

This is a low-level operation on the coordinate representation — useful when chaining transpositions in fifths-aware code.

Parameters:

Name Type Description Default
interval_name str

The starting interval.

required
fifths int

Number of fifths to shift (negative subtracts).

required

Returns:

Type Description
str

The new interval name, or "" if input is invalid.

Example

from tonal_py import Interval Interval.transpose_fifths("4P", 2) '12P'

Source code in src/tonal_py/interval.py
def transpose_fifths(interval_name: str, fifths: int) -> str:
    """Shift an interval by N perfect fifths on the circle of fifths.

    This is a low-level operation on the coordinate representation —
    useful when chaining transpositions in fifths-aware code.

    Args:
        interval_name: The starting interval.
        fifths: Number of fifths to shift (negative subtracts).

    Returns:
        The new interval name, or `""` if input is invalid.

    Example:
        >>> from tonal_py import Interval
        >>> Interval.transpose_fifths("4P", 2)
        '12P'
    """
    ivl = get(interval_name)
    if ivl.empty:
        return ""
    n_fifths, n_octs = ivl.coord[0], ivl.coord[1]
    return _pitch_interval.coord_to_interval((n_fifths + fifths, n_octs)).name