Score analysis

Vertical analysis begins with paeonia.Score.verticalities(). It divides an aligned bar at every onset and release while retaining sustained notes, exact rational offsets, written pitch spelling, and staff ownership. The remaining Score methods derive their results from those time frames and never modify the score.

For example:

score.verticalities(0)
score.sonority_at(0, Fraction(1, 2))
score.harmonic_context(0)
score.voice_leading_to(0)  # bar 0 to bar 1

Objective methods such as paeonia.Score.pitch_class_set(), paeonia.Score.interval_matrix(), paeonia.Score.density(), and paeonia.Score.register_analysis() do not require a tonality. Functional methods use the effective score, voice, and bar tonal context. If selected staves disagree, pass tonality=... explicitly or select compatible staves with staves=[...].

Analysis result types

Time-aware vertical, harmonic, registral, and voice-leading analysis.

The functions in this module inspect model objects without modifying them. Durations and offsets remain exact fractions.Fraction values, and written pitch classes are kept alongside their MIDI pitch classes so that enharmonic spellings are never silently collapsed.

class paeonia.analysis.BassMotion(from_offset, to_offset, from_pitch, to_pitch, semitones)[source]

Bases: object

A change from one sounding bass pitch to the next.

Parameters:
  • from_offset (Fraction)

  • to_offset (Fraction)

  • from_pitch (Pitch)

  • to_pitch (Pitch)

  • semitones (int)

from_offset: Fraction
to_offset: Fraction
from_pitch: Pitch
to_pitch: Pitch
semitones: int
class paeonia.analysis.ChordCandidate(root, quality, symbol, intervals, inversion, confidence, matched_pitch_classes, missing_pitch_classes, extra_pitch_classes, roman_numeral=None)[source]

Bases: object

A possible tertian or suspended interpretation of a sonority.

Parameters:
  • root (PitchClass)

  • quality (str)

  • symbol (str)

  • intervals (frozenset[int])

  • inversion (int | None)

  • confidence (float)

  • matched_pitch_classes (frozenset[int])

  • missing_pitch_classes (frozenset[int])

  • extra_pitch_classes (frozenset[int])

  • roman_numeral (str | None)

root: PitchClass
quality: str
symbol: str
intervals: frozenset[int]
inversion: int | None
confidence: float
matched_pitch_classes: frozenset[int]
missing_pitch_classes: frozenset[int]
extra_pitch_classes: frozenset[int]
roman_numeral: str | None
class paeonia.analysis.DensityPoint(offset, duration, pitch_count, active_staff_count)[source]

Bases: object

The number of sounding pitches and active staves in a time frame.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • pitch_count (int)

  • active_staff_count (int)

offset: Fraction
duration: Fraction
pitch_count: int
active_staff_count: int
class paeonia.analysis.Doubling(midi_pitch_class, count, spellings, staves)[source]

Bases: object

A MIDI pitch class occurring more than once in a verticality.

Parameters:
  • midi_pitch_class (int)

  • count (int)

  • spellings (frozenset[PitchClass])

  • staves (tuple[str, ...])

midi_pitch_class: int
count: int
spellings: frozenset[PitchClass]
staves: tuple[str, ...]
class paeonia.analysis.DoublingSnapshot(offset, duration, doublings)[source]

Bases: object

Pitch-class doublings for one vertical time frame.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • doublings (tuple[Doubling, ...])

offset: Fraction
duration: Fraction
doublings: tuple[Doubling, ...]
class paeonia.analysis.EventReference(staff, bar_index, event_index, pitch_index)[source]

Bases: object

Identify one spelled pitch in a source score event.

Event and pitch indices are local to the referenced bar. Keeping this identity in analysis results lets renderers annotate an exact event or an individual chord pitch without trying to recover it from MIDI value and offset.

Parameters:
  • staff (str)

  • bar_index (int)

  • event_index (int)

  • pitch_index (int)

staff: str
bar_index: int
event_index: int
pitch_index: int
class paeonia.analysis.HarmonicChange(offset, duration, candidate)[source]

Bases: object

A contiguous span assigned the same primary chord interpretation.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • candidate (ChordCandidate | None)

offset: Fraction
duration: Fraction
candidate: ChordCandidate | None
property roman_numeral: str | None

Return this assignment’s functional label, when available.

property symbol: str | None

Return this assignment’s chord symbol, when available.

class paeonia.analysis.HarmonicContext(bar_index, tonality, verticalities, primary, primary_coverage, weighted_candidates, changes, non_chord_tones)[source]

Bases: object

Duration-weighted harmonic interpretation of one score bar.

Parameters:
bar_index: int
tonality: Tonality
verticalities: tuple[Verticality, ...]
primary: ChordCandidate | None
primary_coverage: Fraction
weighted_candidates: tuple[WeightedChord, ...]
changes: tuple[HarmonicChange, ...]
non_chord_tones: tuple[NonChordTone, ...]
class paeonia.analysis.IntervalRelation(first_staff, second_staff, first_pitch, second_pitch, semitones, interval_class, dissonant)[source]

Bases: object

The sounding interval between pitches owned by two staves.

Parameters:
  • first_staff (str)

  • second_staff (str)

  • first_pitch (Pitch)

  • second_pitch (Pitch)

  • semitones (int)

  • interval_class (int)

  • dissonant (bool)

first_staff: str
second_staff: str
first_pitch: Pitch
second_pitch: Pitch
semitones: int
interval_class: int
dissonant: bool
class paeonia.analysis.IntervalSnapshot(offset, duration, intervals)[source]

Bases: object

Cross-staff intervals for one vertical time frame.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • intervals (tuple[IntervalRelation, ...])

offset: Fraction
duration: Fraction
intervals: tuple[IntervalRelation, ...]
class paeonia.analysis.NonChordTone(offset, staff, pitch, kind, source=None)[source]

Bases: object

A pitch outside a local chord interpretation.

Parameters:
  • offset (Fraction)

  • staff (str)

  • pitch (Pitch)

  • kind (Literal['passing', 'neighbor', 'unclassified'])

  • source (EventReference | None)

offset: Fraction
staff: str
pitch: Pitch
kind: Literal['passing', 'neighbor', 'unclassified']
source: EventReference | None
class paeonia.analysis.ParallelMotion(first_staff, second_staff, interval_class)[source]

Bases: object

Parallel perfect fifth or octave between two staff-owned motions.

Parameters:
  • first_staff (str)

  • second_staff (str)

  • interval_class (int)

first_staff: str
second_staff: str
interval_class: int
class paeonia.analysis.PitchClassSet(written, midi)[source]

Bases: object

Both written and sounding representations of a pitch-class set.

Parameters:
  • written (frozenset[PitchClass])

  • midi (frozenset[int])

written: frozenset[PitchClass]
midi: frozenset[int]
class paeonia.analysis.RegisterAnalysis(snapshots, overlaps)[source]

Bases: object

Register snapshots and cross-frame overlaps for one bar.

Parameters:
snapshots: tuple[RegisterSnapshot, ...]
overlaps: tuple[RegisterOverlap, ...]
class paeonia.analysis.RegisterOverlap(offset, upper_staff, lower_staff)[source]

Bases: object

Adjacent ordered staves overlapping across two time frames.

Parameters:
  • offset (Fraction)

  • upper_staff (str)

  • lower_staff (str)

offset: Fraction
upper_staff: str
lower_staff: str
class paeonia.analysis.RegisterSnapshot(offset, duration, bass, soprano, ambitus, spacing, staff_ranges, crossings)[source]

Bases: object

Register, spacing, and voice-crossing data at one offset.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • bass (Pitch | None)

  • soprano (Pitch | None)

  • ambitus (int | None)

  • spacing (tuple[int, ...])

  • staff_ranges (tuple[StaffRange, ...])

  • crossings (tuple[tuple[str, str], ...])

offset: Fraction
duration: Fraction
bass: Pitch | None
soprano: Pitch | None
ambitus: int | None
spacing: tuple[int, ...]
staff_ranges: tuple[StaffRange, ...]
crossings: tuple[tuple[str, str], ...]
class paeonia.analysis.ScaleDegree(staff, pitch, position)[source]

Bases: object

A staff-owned pitch analyzed within a tonality.

Parameters:
staff: str
pitch: Pitch
position: ScalePosition
property number: int

Return the conventional one-based scale-degree number.

class paeonia.analysis.ScaleDegreeSnapshot(offset, duration, degrees)[source]

Bases: object

Scale-degree analyses for one vertical time frame.

Parameters:
  • offset (Fraction)

  • duration (Fraction)

  • degrees (tuple[ScaleDegree, ...])

offset: Fraction
duration: Fraction
degrees: tuple[ScaleDegree, ...]
class paeonia.analysis.StaffPitches(staff, pitches, sources=())[source]

Bases: object

Pitches and source identities sounding in one staff time frame.

Parameters:
staff: str
pitches: tuple[Pitch, ...]
sources: tuple[EventReference, ...]
class paeonia.analysis.StaffRange(staff, lowest, highest)[source]

Bases: object

The lowest and highest pitches sounding on a staff.

Parameters:
staff: str
lowest: Pitch
highest: Pitch
class paeonia.analysis.Verticality(bar_index, offset, duration, staff_pitches)[source]

Bases: object

All pitches sounding over one exact, half-open time interval.

offset and duration are measured in whole-note units relative to the beginning of bar_index. staff_pitches follows score insertion order and includes silent staves with an empty pitch tuple.

Parameters:
  • bar_index (int)

  • offset (Fraction)

  • duration (Fraction)

  • staff_pitches (tuple[StaffPitches, ...])

bar_index: int
offset: Fraction
duration: Fraction
staff_pitches: tuple[StaffPitches, ...]
property by_staff: dict[str, tuple[Pitch, ...]]

Return pitches keyed by staff name in score order.

property pitches: tuple[Pitch, ...]

Return all pitches in staff and chord order.

property sources: tuple[EventReference, ...]

Return source identities in staff and chord order.

property bass: Pitch | None

Return the lowest sounding pitch, or None during silence.

property soprano: Pitch | None

Return the highest sounding pitch, or None during silence.

property pitch_classes: PitchClassSet

Return written and MIDI pitch-class sets for this frame.

property interval_class_vector: tuple[int, int, int, int, int, int]

Return the six-entry interval-class vector of the MIDI set.

property prime_form: tuple[int, ...]

Return a transposition/inversion-normalized pitch-class form.

scale_degrees(tonality)[source]

Analyze every sounding pitch while preserving staff ownership.

Parameters:

tonality (Tonality)

Return type:

tuple[ScaleDegree, …]

interval_matrix(*, dissonant_classes=frozenset({1, 2, 6}))[source]

Return every cross-staff pitch interval in this frame.

Parameters:

dissonant_classes (Collection[int])

Return type:

tuple[IntervalRelation, …]

dissonances(*, dissonant_classes=frozenset({1, 2, 6}))[source]

Return relations classified as dissonant by MIDI interval class.

Parameters:

dissonant_classes (Collection[int])

Return type:

tuple[IntervalRelation, …]

doublings()[source]

Return MIDI pitch classes represented by multiple pitches.

Return type:

tuple[Doubling, …]

chord_candidates(*, tonality=None, limit=5)[source]

Rank common chord templates against the sounding MIDI set.

Parameters:
  • tonality (Tonality | None)

  • limit (int)

Return type:

tuple[ChordCandidate, …]

class paeonia.analysis.VoiceLeadingAnalysis(from_bar, to_bar, motions, common_tones, total_motion, contrary_pairs, parallel_perfects, leaps)[source]

Bases: object

Motion from the final sonority of one bar to the first of another.

Parameters:
  • from_bar (int)

  • to_bar (int)

  • motions (tuple[VoiceMotion, ...])

  • common_tones (tuple[int, ...])

  • total_motion (int)

  • contrary_pairs (tuple[tuple[str, str], ...])

  • parallel_perfects (tuple[ParallelMotion, ...])

  • leaps (tuple[VoiceMotion, ...])

from_bar: int
to_bar: int
motions: tuple[VoiceMotion, ...]
common_tones: tuple[int, ...]
total_motion: int
contrary_pairs: tuple[tuple[str, str], ...]
parallel_perfects: tuple[ParallelMotion, ...]
leaps: tuple[VoiceMotion, ...]
class paeonia.analysis.VoiceMotion(staff, from_pitch, to_pitch, semitones)[source]

Bases: object

One pitch moving between two analyzed sonorities.

Parameters:
  • staff (str)

  • from_pitch (Pitch)

  • to_pitch (Pitch)

  • semitones (int)

staff: str
from_pitch: Pitch
to_pitch: Pitch
semitones: int
class paeonia.analysis.WeightedChord(candidate, duration, coverage)[source]

Bases: object

A chord candidate weighted by its duration within a bar.

Parameters:
candidate: ChordCandidate
duration: Fraction
coverage: Fraction
paeonia.analysis.interval_class_vector(pitch_classes)[source]

Return the standard interval-class vector for unique MIDI classes.

Parameters:

pitch_classes (Iterable[int])

Return type:

tuple[int, int, int, int, int, int]

paeonia.analysis.prime_form(pitch_classes)[source]

Return a canonical form under transposition and inversion.

The most compact rotation of the set and its inversion are transposed to zero; the more tightly left-packed of those two forms is returned.

Parameters:

pitch_classes (Iterable[int])

Return type:

tuple[int, …]