partitura

repository·main·Indexed 18 days ago

https://github.com/cpjku/partitura

A Python package for handling symbolic musical information, optimized for Music Information Retrieval (MIR) and machine learning workflows. It provides tools for loading, manipulating, and exporting musical scores and performances in formats including MusicXML, MIDI, Humdrum (kern), and MEI. Key features include a timeline-based representation of musical elements, conversion of scores to numpy note arrays and scipy sparse piano rolls, and support for score-to-performance alignments via Match files.

Tokens
16.2K
Snippets
52
Records
78
Agent score
61%

What's inside partitura

  1. Overview of Partitura

    main

    Partitura is a lightweight Python package designed for Music Information Retrieval (MIR) researchers. It provides easy access to musical information contained in symbolic music formats (scores and MIDI performances).

    Key features include:

    • Note arrays: Lists of timed pitched events.
    • Pianorolls: 2D time x pitch matrices.
    • Rich musical elements: Support for time/key signatures, performance directives, repeat structures, pitch spellings, and voicing information.
    • Alignment support: Loading score-to-performance alignments.

    While music21 is better suited for computational musicology and complex score manipulation, partitura is optimized for machine learning and deep learning workflows, focusing on fast feature extraction from symbolic data.

  2. Distinguishing between Scores and Performances in MIDI

    main

    Partitura treats MIDI files as either a score or a performance, rather than a single ambiguous entity. This distinction is important because scores contain structural information (key/time signatures, tempo) while performances contain expressive information (dynamics, expressive timing).

    Use the following specific functions to ensure correct loading:

    • For Scores: Use partitura.load_score_midi. This function offers simple quantization for unquantized MIDIs.
    • For Performances: Use partitura.load_performance_midi.

    Note: You should generally not expect a MIDI representation of a performance to be loaded correctly as a Part instance using score-loading functions.

  3. How Partitura represents score information

    main

    Partitura uses a musical ontology inspired by the MusicXML specification. Musical scores are represented as a collection of instances (e.g., Note, Measure, Slur, Rest) attached to a Part instance (representing an instrument).

    The Timeline Model

    Unlike MusicXML where time is often implicit, time is explicit in Partitura. A Part acts as a timeline of discrete timepoints. Each musical element is associated with a Part by specifying its start (and potentially end) times.

    Iterating through elements

    You can filter and iterate over musical elements within a Part by class. Iteration can be performed:

    • From a specific timepoint forward.
    • From a specific timepoint backward.
    • Within a specified time range.

    Example Pattern: To find the measure containing a specific note, you would iterate backwards over elements of class Measure that start at or before the note's start time and select the first match.

  4. How to load musical scores in Partitura

    main

    Partitura provides a generic load_score function that automatically detects the file format (MusicXML, Kern, MIDI, or MEI) and returns a partitura.Score object. You can also use format-specific loaders if preferred.

    To access musical data, you typically iterate through score.parts. Each part contains musical objects like notes, rests, and time signatures.

    import partitura as pt
    
    # Generic loader (recommended)
    score = pt.load_score('path/to/file.musicxml')
    
    # Format-specific loaders
    score = pt.load_musicxml('path/to/file.musicxml')
    score = pt.load_kern('path/to/file.krn')
    score = pt.load_mei('path/to/file.mei')
  5. Integrating Partitura with music21

    main

    You can use a hybrid workflow by combining music21 and partitura. This is useful if you need the advanced manipulation capabilities of music21 but want the fast feature extraction of partitura.

    Workflow:

    1. Load a score in music21.
    2. Perform complex manipulations/modifications using music21 APIs.
    3. Use the music21 to partitura converter to transform the score into a Partitura object.
    4. Extract MIR features using partitura.
  6. What is a TimePoint?

    main

    A TimePoint represents a specific temporal position within a Part. They are used to track the start and end of musical elements like notes or rests.

    Key Attributes:

    • t: The integer time associated with the point.
    • quarter: The duration of a quarter note at this point.
    • starting_objects: A dictionary grouping objects that start at this time by their class.
    • ending_objects: A dictionary grouping objects that end at this time by their class.
    • prev / next: Pointers to the preceding and succeeding TimePoint in the timeline.

    Common Methods:

    • iter_starting(cls, include_subclasses=False): Yields objects of type cls starting at this point.
    • iter_ending(cls, include_subclasses=False): Yields objects of type cls ending at this point.
    • iter_prev(...) / iter_next(...): Iterates backwards or forwards through the timeline starting from this point.
  7. What is a TimedObject?

    main

    A TimedObject is the base class for any musical element that occupies a span of time. Every TimedObject has a start and an end attribute, both of which are TimePoint instances.

    Key Properties:

    • duration: Returns the duration of the object in divisions (end.t - start.t). Returns None if the object is not yet attached to a Part (i.e., start or end is None).
  8. Represent performance directions with Direction classes

    main

    Partitura provides a hierarchy of classes to represent various musical performance directions (e.g., dynamics, tempo, pedal markings) within a score. All directions inherit from the Direction base class, which is a TimedObject containing text, raw_text, and an optional staff association.

    Key direction types include:

    • LoudnessDirection: For volume markings.
    • TempoDirection: For speed markings.
    • ArticulationDirection: For articulation markings.
    • PedalDirection: For pedal markings (e.g., SustainPedalDirection).
    • DynamicDirection: For dynamic markings (e.g., DynamicLoudnessDirection, DynamicTempoDirection).
    • ConstantDirection: For directions that remain unchanged (e.g., ConstantLoudnessDirection, ConstantTempoDirection).
    • ImpulsiveDirection: For sudden changes (e.g., ImpulsiveLoudnessDirection).

    Specialized classes like DynamicLoudnessDirection include additional properties like wedge (boolean). ResetTempoDirection can be used to find the previous ConstantTempoDirection in the timeline using its .reference_tempo property.

  9. Use GenericNote for common note attributes

    main

    The GenericNote class provides the foundation for notes, rests, and unpitched notes. It manages attributes like voice, staff, id, and stem_direction.

    Symbolic Duration: GenericNote supports a symbolic_duration property. This can be a dictionary specifying:

    • type: e.g., 'quarter', 'half'.
    • dots: integer number of dots.
    • actual_notes / normal_notes: used for rhythmical tuplets.

    If symbolic_duration is not manually set, the class attempts to estimate it dynamically based on the numeric duration and the quarter duration of the note's start timepoint.

  10. Map timeline times to musical properties

    main

    The Part class provides several property-based mapping functions (interpolators) that allow you to query the musical state at any given timeline time t. These functions accept either a single scalar time or a NumPy array of times.

    Available Mapping Properties:

    • time_signature_map: Maps time to (beats, beat_type, musical_beats).
    • key_signature_map: Maps time to the key (fifths) and mode.
    • clef_map: Maps time to the clef active on each staff (returns an array of clef info per staff).
    • measure_map: Maps time to the (start, end) of the containing measure.
    • measure_number_map: Maps time to the integer measure number.
    • metrical_position_map: Maps time to the relative position within a measure.
    • beat_map: Maps timeline times to musical beat times.
    • inv_beat_map: Maps beat times back to timeline times.
    • quarter_map: Maps timeline times to quarter note times.
    • inv_quarter_map: Maps quarter note times back to timeline times.
    • constant_dynamics_map: Maps time to the active ConstantLoudnessDirection.
    • variable_dynamics_map: Maps time to impulsive/variable Dynamics markings.
    • tempo_directions_map: Maps time to the active TempoDirection.
  11. Represent an instrument part with PerformedPart

    main

    A PerformedPart represents all notes and continuous control parameters (like sustain pedal) for a single instrument.

    Key features:

    • Notes: Stored as a list of PerformedNote objects (or dictionaries).
    • Controls: Continuous control changes (e.g., {'type': 'sustain_pedal', 'time': 1.5, 'value': 127}).
    • Sustain Pedal Logic: You can set a sustain_pedal_threshold (0-127). When the pedal value is above this threshold, the part automatically calculates a sound_off time for notes, extending their duration until the pedal is released.
    • MIDI Encoding: Supports ppq (parts per quarter) and mpq (microseconds per quarter) for tick-based calculations.
    from partitura.performance import PerformedPart
    
    part = PerformedPart(
        id="piano_01",
        part_name="Piano",
        notes=[{"midi_pitch": 60, "note_on": 0.0, "note_off": 1.0, "velocity": 80}],
        controls=[{"type": "sustain_pedal", "time": 0.5, "value": 127}],
        sustain_pedal_threshold=64
    )
  12. Understand the Part object

    main

    The Part object is the central representation of a musical score in Partitura.

    Key characteristics:

    • It is a timeline object where time is measured in divs (integer units).
    • Its elements are TimedObjects (e.g., notes, measures) which possess a start and end time.
    • It provides methods to map between divs and musical units like beats or quarters.

    Working with Notes

    Each Part contains a notes list. Notes can be added or removed using part.add(note_object, start, end) and part.remove(note_object). Note timing must be specified in divs.

    import partitura as pt
    
    # Load a score
    part = pt.load_musicxml("path/to/score.xml")[0]
    
    # Create and add a new note
    a_new_note = pt.score.Note(id='n04', step='A', octave=4, voice=1)
    part.add(a_new_note, start=3, end=15)  # timing in divs
    
    # Remove a note
    part.remove(a_new_note)