Skip to content

Pitch

Pitch

Pitch(
    pitch: Union[Pitch, int, float, str, None] = 60,
    alt: Union[int, float, None] = None,
    octave: Union[int, None] = None,
    accidental_chars: Optional[str] = None,
)

Represents a symbolic musical pitch.

A pitch is represented by a key_num and an alt. The key_num is a number that corresponds to the MIDI convention where C4 is 60, C# is 61, etc., but generalized to floats (60.5 would be C4-quarter-tone-sharp). The alt is an alteration, where +1 represents a sharp and -1 represents a flat. Alterations can also be, for example, 2 (double-sharp) or -0.5 (quarter-tone flat). The symbolic note name is derived by subtracting alt from key_num.

E.g., C#4 has key_num=61, alt=1, so 61-1 gives us 60, corresponding to note name C. A Db has the same key_num=61, but alt=-1, and 61-(-1) gives us 62, corresponding to note name D. There is no representation for the “natural sign” (other than alt=0, which could imply no accidental) or “courtesy accidentals.” Because accidentals normally “stick” within a measure or are implied by key signatures, accidentals are often omitted in the score presentation. Nonetheless, these implied accidentals are encoded in the alt attribute and key_num is the intended pitch with the accidental applied.

key_num and alt must always satisfy the invariant that (key_num - alt) % 12 corresponds to one of the pitch classes corresponding to {C, D, E, F, G, A, B}. The constructor enforces this invariant via _fix_alteration: key_num always takes priority, and if the given alt does not produce a diatonic pitch, alt is replaced with the smallest-magnitude value that does. Ties between enharmonic spellings of the same key_num (e.g. C#/Db) default to the spellings C#, Eb, F#, Ab, and Bb.

Author: Roger B. Dannenberg

Parameters:

  • pitch (Union[int, float, str, None], default: 60 ) –

    Optional MIDI key_num or string Pitch name. Syntax is A-G followed by accidentals (see accidental_chars below) followed by octave number. (Defaults to 60)

  • alt (Union[int, float, None], default: None ) –

    If pitch is a number, alt is an optional alteration (Defaults to 0). If pitch - alt does not result in a diatonic pitch number, alt is adjusted, normally choosing spellings C#, Eb, F#, Ab, and Bb. If pitch is a string, alt must be None. If pitch is itself an instance of Pitch, alt is ignored (the source Pitch's own alt is used).

  • octave (Optional[int], default: None ) –

    If pitch is a string without an octave specification and octave is an int, then octave is used to specify the octave, where 4 denotes the key_num range 60 through 71. The octave defaults to -1, which yields pitch class key_nums 0-11.

  • accidental_chars (Optional[str], default: None ) –

    Allows parsing of pitch names with customized accidental characters. The value is a tuple or list consisting of a string of flat characters and a string of sharp characters, e.g. `["fb", "s#"]. (Defaults to None, which admits '♭', 'b' or '-' for flat, and '♯', '#', and '+' for sharp, but does not accept 'f' and 's'.)

Attributes:

  • key_num (float) –

    MIDI key number, e.g., C4 = 60, generalized to float.

  • alt (float) –

    Alteration, e.g., flat = -1.

Examples:

>>> p = Pitch(64)
>>> p
Pitch(name='E4', key_num=64)
>>> p.octave
4
>>> p = Pitch("E4")
>>> p.octave
4
>>> p = Pitch("F#################2")
>>> p.alt
17
>>> p.octave
2
>>> p = Pitch("E--------------------4")
>>> p.alt
-20
>>> p.octave
4
>>> p.register
2
>>> Pitch(61.5, alt=1.5)
Pitch(name='C?4', key_num=61.5)

key_num - alt must be a diatonic pitch number. If not, key_num gets priority and alt is adjusted to the smallest valid value. Here, alt is adjusted to 0, which preserves the key_num of 60:

>>> Pitch(60, alt=1.4)
Pitch(name='C4', key_num=60)
Source code in amads/core/pitch.py
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
def __init__(self,
             pitch: Union["Pitch", int, float, str, None] = 60,
             alt: Union[int, float, None] = None,
             octave: Union[int, None] = None,
             accidental_chars: Optional[str] = None):
    if isinstance(pitch, str):
        if alt is not None:
            raise ValueError("If pitch is a string, alt must be None")
        self.key_num, self.alt = Pitch.from_name(
            pitch, octave, accidental_chars
        )
    elif isinstance(pitch, Pitch):
        self.key_num = pitch.key_num
        self.alt = pitch.alt
    elif pitch is None:
        self.key_num = None
        self.alt = (0 if alt is None else alt)
    else:  # pitch is a number (int or float)
        # this will raise a ValueError if pitch is not some kind of number:
        pitch = float(pitch)  # converts numpy.int64, nympy.floating, etc.
        if pitch.is_integer():  # for nicer printing
            pitch = int(pitch)  # pitch numbers as integers.
        self.key_num = pitch
        self.alt = (0 if alt is None else alt)
        self._fix_alteration()

Attributes

step property

step: str

The diatonic name of the pitch: A, B, C, D, E, F, or G.

The diatonic name corresponds to letter name without accidentals.

Returns:

  • str

    The name of the pitch, a letter in "A" through "G".

name property

name: str

The string name including accidentals (# or b).

The octave number is omitted. If alt is not an integer, return the step name concatenated with "?". See also get_name, which accepts a parameter to specify accidental characters.

name_with_octave property

name_with_octave: str

The string name with octave, e.g., "C4", "B#3", etc.

The octave number is calculated by

(key_num - alteration) // 12 + 1  # (integer division)

and refers to the pitch before alteration, e.g., C4 is enharmonic to B#3 and represents the same (more or less) pitch even though the written octave numbers differ.

See also get_name_with_octave, which accepts a parameter to specify custom characters to represent accidentals.

octave property

octave: int

The octave number of the note name.

The note name is based on key_num - alt, e.g., C4 has octave 4 while B#3 has octave 3.

pitch_class property

pitch_class: int

The pitch class of the note, e.g., 0, 1, 2, ..., 11.

The pitch class is the key_num modulo 12, which gives the class of this pitch in the range 0-11. If the key_num is non-integer, it is rounded.

Returns:

  • int

    The pitch class of the note.

fifths_from_c property

fifths_from_c: float

The position of this pitch's spelling on the line of fifths.

C is 0. Each step sharpward (C->G->D->A->E->B->F#->...) is +1, and each step flatward (C->F->Bb->Eb->Ab->Db->Gb->...) is -1. Unlike pitch_class, this distinguishes enharmonic spellings: F# is +6 while Gb is -6, even though both have the same pitch_class.

The result is an integer unless alt is non-integer (e.g. for a quarter-tone accidental).

Returns:

  • float

    The signed number of fifths from C, sharpward positive.

Examples:

>>> Pitch("C4").fifths_from_c
0

Octave invariant.

>>> Pitch("C9").fifths_from_c
0
>>> Pitch("G4").fifths_from_c
1
>>> Pitch("F4").fifths_from_c
-1
>>> Pitch("F#4").fifths_from_c
6
>>> Pitch("Gb4").fifths_from_c
-6
>>> Pitch("B##3").fifths_from_c
19

register property

register: int

Returns the absolute octave number based on floor(key_num).

Both C4 and B#3 have register 4.

Functions

as_tuple

as_tuple()

Return a tuple representation of the Pitch instance.

Returns:

  • tuple

    A tuple containing the key_num and alt values.

Source code in amads/core/pitch.py
212
213
214
215
216
217
218
219
220
def as_tuple(self):
    """Return a tuple representation of the `Pitch` instance.

    Returns
    -------
    tuple
        A tuple containing the `key_num` and `alt` values.
    """
    return (self.key_num, self.alt)

from_name classmethod

from_name(
    name: str,
    octave: Optional[float] = -1,
    accidental_chars: Optional[str] = None,
) -> Tuple[float, float]

Converts a string like "Bb" to a (pitch, alt) tuple.

For example, converts "Bb" to (10, -1). If the string has an octave number or octave is given, the octave will be applied, e.g., "C4" yields (60, 0). octave takes effect if it is a number and name does not include an octave. If both name has no octave and octave is None, the octave is -1, yielding pitch class numbers 0-11.

The first character must be one of the unmodified base pitch names: C, D, E, F, G, A, B (not case-sensitive).

Subsequent characters must indicate a single accidental type: one of '♭', 'b' or '-' for flat, and '♯', '#', and '+' for sharp, unless accidental_chars specified exactly the acceptable flat and sharp chars, e.g., "fs" indicates 'f' for flat, 's' for sharp.

Note that 's' is not a default accidental type as it is ambiguous: 'Fs' probably indicates F#, but Es is more likely Eb (German).

Also unsupported are: mixtures of sharps and flats (e.g., B#b); symbols for double sharps, quarter sharps, naturals, etc.; any other characters (except space, tab and underscore, which are allowed but ignored).

Following accidentals (if any) is an optional single-digit octave number. Note that MIDI goes below C0; if the octave number is omitted, the octave will be -1, which corresponds to pitch class numbers 0-11 and octave -1 (which you can also specify as octave).

Instructive error messages are given for invalid input.

Parameters:

  • name (str) –

    The string representation of the pitch name.

  • octave (Optional[float], default: -1 ) –

    The octave number if not specified in the name. (Defaults to -1)

  • accidental_chars (Optional[tuple[str, str]], default: None ) –

    The characters to use for flat and sharp accidentals. This is a tuple of two strings, where the first is all possible flat characters, and the second is all possible sharp characters. (Defaults to None, which admits '♭', 'b' or '-' for flat, and '♯', '#', and '+' for sharp.)

Returns:

  • Tuple[float, float]

    A tuple containing the key_num and alt values.

Source code in amads/core/pitch.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
@classmethod
def from_name(cls, name: str,
              octave: Optional[float] = -1,
              accidental_chars: Optional[str] = None
              ) -> Tuple[float, float]:
    """
    Converts a string like "Bb" to a (pitch, alt) tuple.

    For example, converts "Bb" to (10, -1).
    If the string has an octave number or octave is given, the
    octave will be applied, e.g., "C4" yields (60, 0). `octave` takes
    effect if it is a number and name does not include an octave.
    If both `name` has no octave and `octave` is None, the octave is
    -1, yielding pitch class numbers 0-11.

    The first character must be one of the unmodified base pitch names:
    C, D, E, F, G, A, B (not case-sensitive).

    Subsequent characters must indicate a single accidental type:
    one of '♭', 'b' or '-' for flat, and '♯', '#', and '+' for sharp,
    unless accidental_chars specified exactly the acceptable flat
    and sharp chars, e.g., "fs" indicates 'f' for flat, 's' for sharp.

    Note that 's' is not a default accidental type as it is ambiguous:
    'Fs' probably indicates F#, but Es is more likely Eb (German).

    Also unsupported are:
    mixtures of sharps and flats (e.g., B#b);
    symbols for double sharps, quarter sharps, naturals, etc.;
    any other characters (except space, tab and underscore,
    which are allowed but ignored).

    Following accidentals (if any) is an optional single-digit octave
    number. Note that MIDI goes below C0; if the octave number is
    omitted, the octave will be -1, which corresponds to pitch class
    numbers 0-11 and octave -1 (which you can also specify as octave).

    Instructive error messages are given for invalid input.

    Parameters
    ----------
    name : str
        The string representation of the pitch name.
    octave : Optional[float], optional
        The octave number if not specified in the name.
        (Defaults to -1)
    accidental_chars : Optional[tuple[str, str]], optional
        The characters to use for flat and sharp accidentals.
        This is a tuple of two strings, where the first is all possible
        flat characters, and the second is all possible sharp characters.
        (Defaults to None, which admits '♭', 'b' or '-' for flat, and
        '♯', '#', and '+' for sharp.)

    Returns
    -------
    Tuple[float, float]
        A tuple containing the `key_num` and `alt` values.
    """
    name = name.replace(" ", "").replace("\t", "").replace("_", "")
    if name == "":
        return 60, 0
    pitch_base = name[0].upper()  # error if non-string
    if pitch_base not in "ABCDEFG":
        raise ValueError("Invalid first character: must be one of ABCDEFG")
    pitch_class = LETTER_TO_NUMBER[pitch_base]

    name = name[1:]  # trim the note letter
    if len(name) > 0:
        if name[-1].isdigit():  # final character indicates octave
            octave = int(name[-1])  # overrides octave parameter
            name = name[:-1]  # remove octave from working
    if octave is None:  # no octave given in name or 2nd parameter
        octave = -1

    # parse the accidentals, if any
    if accidental_chars:
        flat_chars = accidental_chars[0]
        sharp_chars = accidental_chars[1]
    else:
        flat_chars = "♭b-"  # first is unicode flat
        sharp_chars = "♯#+"  # first is unicode sharp
    if all(x in flat_chars for x in name):  # flats
        alteration = -len(name)
    elif all(x in sharp_chars for x in name):  # sharps
        alteration = len(name)
    else:
        raise ValueError("Invalid accidentals: must be only " +
                         f"{flat_chars} or {sharp_chars}.")

    # note that octave applies to pitch_class before alteration, so B#3=C4
    return pitch_class + 12 * (octave + 1) + alteration, alteration

get_name

get_name(accidental_chars: str = 'b#') -> str

Return string name including accidentals (# or b) but no octave.

See the name property for details.

Parameters:

  • accidental_chars (str, default: 'b#' ) –

    The characters to use for flat and sharp accidentals. (Defaults to "b#")

Returns:

  • str

    The string representation of the pitch name, including accidentals.

Source code in amads/core/pitch.py
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def get_name(self, accidental_chars: str = "b#") -> str:
    """Return string name including accidentals (# or b) but no octave.

    See the `name` property for details.

    Parameters
    ----------
    accidental_chars : str, optional
        The characters to use for flat and sharp accidentals.
        (Defaults to "b#")

    Returns
    -------
    str
        The string representation of the pitch name, including accidentals.
    """
    accidentals = "?"
    sharp_char = (accidental_chars[1] if len(accidental_chars) > 1 else "#")
    if round(self.alt) == self.alt:  # an integer value
        if self.alt > 0:
            accidentals = sharp_char * round(self.alt)
        elif self.alt < 0:
            accidentals = accidental_chars[0] * round(-self.alt)
        else:
            accidentals = ""  # natural
    return self.step + accidentals

get_name_with_octave

get_name_with_octave(accidental_chars: str = 'b#') -> str

Return string name with octave, e.g., C4, B#3, etc.

See the name_with_octave property for details.

Parameters:

  • accidental_chars (str, default: 'b#' ) –

    The characters to use for flat and sharp accidentals. (Defaults to "b#")

Returns:

  • str

    The string representation of the pitch name with octave.

Source code in amads/core/pitch.py
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
def get_name_with_octave(self, accidental_chars: str = "b#") -> str:
    """Return string name with octave, e.g., C4, B#3, etc.

    See the [name_with_octave][amads.core.pitch.Pitch.name_with_octave]
    property for details.

    Parameters
    ----------
    accidental_chars : str, optional
        The characters to use for flat and sharp accidentals.
        (Defaults to "b#")

    Returns
    -------
    str
        The string representation of the pitch name with octave.
    """
    return ("unpitched" if self.key_num is None
                        else self.name + str(self.octave))

enharmonic

enharmonic() -> Pitch

Construct an enharmonic equivalent.

If alt is non-zero, return a Pitch where alt is zero or has the opposite sign and where alt is minimized. E.g. enharmonic(Cbb) is A# (not Bb). If alt is zero, return a Pitch with alt of +1 or -1 if possible. Otherwise, return a Pitch with alt of -2 (Ebb, Abb or Bbb). Note the difference between this and simplest_enharmonic.

Returns:

  • Pitch

    A new Pitch object representing the enharmonic equivalent.

Examples:

>>> Pitch("C4").enharmonic()
Pitch(name='B#3', key_num=60)
>>> Pitch("B3").enharmonic()
Pitch(name='Cb4', key_num=59)
>>> Pitch("B#3").enharmonic()
Pitch(name='C4', key_num=60)
>>> bds = Pitch("B##3")
>>> bds.enharmonic() # change of direction
Pitch(name='Db4', key_num=61)
>>> bds.upper_enharmonic()  # note the difference
Pitch(name='C#4', key_num=61)
>>> Pitch("Dbb4").enharmonic()
Pitch(name='C4', key_num=60)
Source code in amads/core/pitch.py
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
def enharmonic(self) -> "Pitch":
    """Construct an enharmonic equivalent.

    If `alt` is non-zero, return a Pitch where `alt` is zero
    or has the opposite sign and where `alt` is minimized. E.g.
    enharmonic(Cbb) is A# (not Bb). If alt is zero, return a
    Pitch with alt of +1 or -1 if possible. Otherwise, return
    a Pitch with alt of -2 (Ebb, Abb or Bbb).
    Note the difference between this and `simplest_enharmonic`.

    Returns
    -------
    Pitch
        A new Pitch object representing the enharmonic equivalent.

    Examples
    --------
    >>> Pitch("C4").enharmonic()
    Pitch(name='B#3', key_num=60)

    >>> Pitch("B3").enharmonic()
    Pitch(name='Cb4', key_num=59)

    >>> Pitch("B#3").enharmonic()
    Pitch(name='C4', key_num=60)

    >>> bds = Pitch("B##3")
    >>> bds.enharmonic() # change of direction
    Pitch(name='Db4', key_num=61)

    >>> bds.upper_enharmonic()  # note the difference
    Pitch(name='C#4', key_num=61)

    >>> Pitch("Dbb4").enharmonic()
    Pitch(name='C4', key_num=60)
    """
    alt = self.alt
    unaltered = round(self.key_num - alt)
    if alt < 0:
        while alt < 0 or (unaltered % 12) not in [0, 2, 4, 5, 7, 9, 11]:
            unaltered -= 1
            alt += 1
    elif alt > 0:
        while alt > 0 or (unaltered % 12) not in [0, 2, 4, 5, 7, 9, 11]:
            unaltered += 1
            alt -= 1
    else:  # alt == 0
        unaltered = unaltered % 12
        if unaltered in [0, 5]:  # C->B#, F->E#
            alt = 1
        elif unaltered in [11, 4]:  # B->Cb, E->Fb
            alt = -1
        else:  # A->Bbb, D->Ebb, G->Abb
            alt = -2
    return Pitch(self.key_num, alt)

simplest_enharmonic

simplest_enharmonic(sharp_or_flat: Optional[str] = 'default') -> Pitch

Create Pitch object with the simplest enharmonic representation.

I.e., if there exists an enharmonic-equivalent pitch with no alterations, then use that. If the Pitch is already in simplest form (e.g., C4), it is simply returned. If an alteration is needed, then use sharps or flats depending on sharp_or_flat. If sharp_or_flat is omitted, the same enharmonic choice as the Pitch constructor is used (C#, Eb, F#, Ab, and Bb).

Parameters:

  • sharp_or_flat (Optional[str], default: 'default' ) –

    This is only relevant if the pitch needs an alteration, otherwise it is unused. The value can be "sharp" (use sharps), "flat" (use flats), and otherwise use the same enharmonic choice as the Pitch constructor.

Examples:

>>> bds = Pitch("B##3")
>>> bds.simplest_enharmonic()
Pitch(name='C#4', key_num=61)
>>> bds.simplest_enharmonic(sharp_or_flat="flat")
Pitch(name='Db4', key_num=61)
>>> Pitch("C4").simplest_enharmonic()
Pitch(name='C4', key_num=60)

Returns:

  • Pitch

    A Pitch object representing the enharmonic equivalent.

Source code in amads/core/pitch.py
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
def simplest_enharmonic(self,
        sharp_or_flat: Optional[str] = 'default') -> "Pitch":
    """
    Create Pitch object with the simplest enharmonic representation.

    I.e., if there exists an enharmonic-equivalent pitch with no
    alterations, then use that. If the Pitch is already in simplest
    form (e.g., C4), it is simply returned. If an alteration is
    needed, then use sharps or flats depending on `sharp_or_flat`.
    If `sharp_or_flat` is omitted, the same enharmonic choice
    as the Pitch constructor is used (C#, Eb, F#, Ab, and Bb).

    Parameters
    ----------
    sharp_or_flat: str
        This is only relevant if the pitch needs an alteration, otherwise
        it is unused. The value can be "sharp" (use sharps), "flat" (use
        flats), and otherwise use the same enharmonic choice as the Pitch
        constructor.

    Examples
    --------

    >>> bds = Pitch("B##3")
    >>> bds.simplest_enharmonic()
    Pitch(name='C#4', key_num=61)

    >>> bds.simplest_enharmonic(sharp_or_flat="flat")
    Pitch(name='Db4', key_num=61)

    >>> Pitch("C4").simplest_enharmonic()
    Pitch(name='C4', key_num=60)

    Returns
    -------
    Pitch
        A Pitch object representing the enharmonic equivalent.
    """
    if not self.alt:
        return self

    if self.pitch_class in [0, 2, 4, 5, 7, 9, 11]:  # C, D, E, F, G, A, B
        return Pitch(self.key_num)
    elif sharp_or_flat == "sharp":  # unaltered in 1, 3, 6, 8, 10
        return Pitch(self.key_num, 1)
    elif sharp_or_flat == "flat":
        return Pitch(self.key_num, -1)
    else:  # let Pitch figure out which enharmonic spelling (alt) to use:
        return Pitch(self.key_num)

upper_enharmonic

upper_enharmonic() -> Pitch

Return the enharmonic based on the note name above.

The result will have the next higher diatonic name with alt accordingly decreased by 1 or 2, e.g., C#->Db, C##->D, Cb->Dbbb.

Returns:

  • Pitch

    A Pitch object representing the upper enharmonic equivalent.

Examples:

>>> bds = Pitch("B##3")
>>> bds
Pitch(name='B##3', key_num=61)
>>> cis = bds.upper_enharmonic()
>>> cis
Pitch(name='C#4', key_num=61)
>>> des = cis.upper_enharmonic()
>>> des
Pitch(name='Db4', key_num=61)
>>> des.upper_enharmonic()
Pitch(name='Ebbb4', key_num=61)
>>> Pitch("D4").upper_enharmonic()
Pitch(name='Ebb4', key_num=62)
Source code in amads/core/pitch.py
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
def upper_enharmonic(self) -> "Pitch":
    """
    Return the enharmonic based on the note name above.

    The result will have the next higher diatonic name
    with `alt` accordingly decreased by 1 or 2, e.g.,
    C#->Db, C##->D, Cb->Dbbb.

    Returns
    -------
    Pitch
        A Pitch object representing the upper enharmonic equivalent.


    Examples
    --------
    >>> bds = Pitch("B##3")
    >>> bds
    Pitch(name='B##3', key_num=61)

    >>> cis = bds.upper_enharmonic()
    >>> cis
    Pitch(name='C#4', key_num=61)

    >>> des = cis.upper_enharmonic()
    >>> des
    Pitch(name='Db4', key_num=61)

    >>> des.upper_enharmonic()
    Pitch(name='Ebbb4', key_num=61)

    >>> Pitch("D4").upper_enharmonic()
    Pitch(name='Ebb4', key_num=62)

    """
    alt = self.alt
    unaltered = round(self.key_num - alt) % 12
    if unaltered in [0, 2, 5, 7, 9]:  # C->D, D->E, F->G, G->A, A->B
        alt -= 2
    else:  # E->F, B->C
        alt -= 1
    return Pitch(self.key_num, alt)

lower_enharmonic

lower_enharmonic() -> Pitch

Return the enharmonic based on the note name below.

The result will have the next lower diatonic name with alt accordingly increased by 1 or 2, e.g., Db->C#, D->C##, D#->C###.

Returns:

  • Pitch

    A Pitch object representing the lower enharmonic equivalent.

Examples:

>>> Pitch("Db4").lower_enharmonic()
Pitch(name='C#4', key_num=61)
>>> Pitch("D4").lower_enharmonic()
Pitch(name='C##4', key_num=62)
>>> Pitch("C#4").lower_enharmonic()
Pitch(name='B##3', key_num=61)
Source code in amads/core/pitch.py
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
def lower_enharmonic(self) -> "Pitch":
    """Return the enharmonic based on the note name below.

    The result will have the next lower diatonic name
    with `alt` accordingly increased by 1 or 2, e.g.,
    Db->C#, D->C##, D#->C###.

    Returns
    -------
    Pitch
        A Pitch object representing the lower enharmonic equivalent.

    Examples
    --------
    >>> Pitch("Db4").lower_enharmonic()
    Pitch(name='C#4', key_num=61)

    >>> Pitch("D4").lower_enharmonic()
    Pitch(name='C##4', key_num=62)

    >>> Pitch("C#4").lower_enharmonic()
    Pitch(name='B##3', key_num=61)

    """
    alt = self.alt
    unaltered = round(self.key_num - alt) % 12
    if unaltered in [2, 4, 7, 9, 11]:  # D->C, E->D, G->F, A->G, B->A
        alt += 2
    else:  # F->E, C->B
        alt += 1
    return Pitch(self.key_num, alt)

PitchCollection dataclass

PitchCollection(pitches: list[Pitch])

Combined representations of more than one pitch. Differs from Chord which has onset, duration, and contains Notes, not Pitches.

Parameters:

  • pitches (list[Pitch]) –

    A list of Pitch instances.

Attributes:

  • pitches (list[Pitch]) –

    A list of Pitch instances.

Examples:

>>> test_case = ['G#4', 'G#4', 'B4', 'D4', 'F4', 'Ab4']
>>> pitches = [Pitch(p) for p in test_case]
>>> pitches_gathered = PitchCollection(pitches)
>>> pitches_gathered.pitch_name_multiset
['G#4', 'G#4', 'B4', 'D4', 'F4', 'Ab4']
>>> pitches_gathered.pitch_num_multiset
[68, 68, 71, 62, 65, 68]
>>> pitches_gathered.pitch_class_multiset
[2, 5, 8, 8, 8, 11]
>>> pitches_gathered.pitch_class_set
[2, 5, 8, 11]
>>> pitches_gathered.pitch_class_vector
(0, 0, 1, 0, 0, 1, 0, 0, 3, 0, 0, 1)
>>> pitches_gathered.pitch_class_indicator_vector
(0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1)

Attributes

pitch_num_multiset property

pitch_num_multiset

Return a list of pitch numbers from the pitches in the collection.

pitch_name_multiset property

pitch_name_multiset

Return a list of pitch names with octaves from the pitches in the collection.

pitch_class_multiset property

pitch_class_multiset

Return a sorted list of pitch classes from the pitches in the collection, including duplicates.

pitch_class_set property

pitch_class_set

Return a sorted list of pitch classes from the pitches in the collection without duplicates.

pitch_class_vector property

pitch_class_vector

Return a pitch class vector (12-dimensional) representing the count of each pitch class in the collection.

pitch_class_indicator_vector property

pitch_class_indicator_vector

Return a pitch class indicator vector (12-dimensional) representing the presence (1) or absence (0) of each pitch class in the collection.

pitches_from_c property

pitches_from_c

Return a list of each pitch's fifths_from_c value, in the same order as pitches (duplicates included, unsorted).

pitches_from_c_centroid property

pitches_from_c_centroid

Return the arithmetic mean of pitches_from_c, i.e. the "centre of mass" of this collection's pitches on the line of fifths. Duplicate pitches are weighted by their multiplicity (each occurrence counts separately, matching pitches_from_c/pitch_num_multiset, as opposed to pitch_class_set-style deduplication).

Raises:

  • ValueError

    If the collection has no pitches.

Examples:

>>> test_case = ['G#4', 'G#4', 'B4', 'D4', 'F4', 'Ab4']
>>> pitches = [Pitch(p) for p in test_case]
>>> pitches_gathered = PitchCollection(pitches)
>>> pitches_gathered.pitches_from_c
[8, 8, 5, 2, -1, -4]
>>> pitches_gathered.pitches_from_c_centroid
3.0

Note how this changes with spelling

>>> test_case = ['G#4', 'G#4', 'B4', 'D4', 'F4', 'G#4']
>>> pitches = [Pitch(p) for p in test_case]
>>> pitches_gathered = PitchCollection(pitches)
>>> pitches_gathered.pitches_from_c
[8, 8, 5, 2, -1, 8]
>>> pitches_gathered.pitches_from_c_centroid
5.0