Source code for partitura.io.exportparangonada

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This module contains methods for saving Parangonada csv files
"""

import os

import numpy as np

from typing import Union, List, Iterable, Tuple, Optional

from partitura.score import ScoreLike
from partitura.performance import PerformanceLike, Performance, PerformedPart

from partitura.utils import ensure_notearray

from partitura.utils.misc import PathLike, deprecated_alias

__all__ = [
    "save_parangonada_csv",
    "save_parangonada_alignment",
]


def alignment_dicts_to_array(alignment: List[dict]) -> np.ndarray:
    """
    create structured array from list of dicts type alignment.

    Parameters
    ----------
    alignment : list
        A list of note alignment dictionaries.

    Returns
    -------
    alignarray : structured ndarray
        Structured array containing note alignment.
    """
    fields = [
        ("idx", "i4"),
        ("matchtype", "U256"),
        ("partid", "U256"),
        ("ppartid", "U256"),
    ]

    array = []
    # for all dicts create an appropriate entry in an array:
    # match = 0, deletion  = 1, insertion = 2
    for no, i in enumerate(alignment):
        if i["label"] == "match":
            array.append((no, "0", i["score_id"], str(i["performance_id"])))
        elif i["label"] == "insertion":
            array.append((no, "2", "undefined", str(i["performance_id"])))
        elif i["label"] == "deletion":
            array.append((no, "1", i["score_id"], "undefined"))

    alignarray = np.array(array, dtype=fields)

    return alignarray


[docs]@deprecated_alias( spart="score_data", ppart="performance_data", align="alignment", ) def save_parangonada_csv( alignment: List[dict], performance_data: Union[PerformanceLike, np.ndarray], score_data: Union[ScoreLike, np.ndarray], outdir: Optional[PathLike] = None, zalign: Optional[List[dict]] = None, feature: Optional[List[dict]] = None, ) -> Optional[Tuple[np.ndarray]]: """ Save an alignment for visualization with parangonda. Parameters ---------- alignment : list A list of note alignment dictionaries. performance_data : Performance, PerformedPart, structured ndarray The performance information score_data : ScoreLike The musical score. A :class:`partitura.score.Score` object, a :class:`partitura.score.Part`, a :class:`partitura.score.PartGroup` or a list of these. outdir : PathLike A directory to save the files into. ppart : PerformedPart, structured ndarray A PerformedPart or its note_array. zalign : list, optional A second list of note alignment dictionaries. feature : list, optional A list of expressive feature dictionaries. Returns ------- perf_note_array : np.ndarray The performance note array. Only returned if `outdir` is None. score_note_array: np.ndarray The note array from the score. Only returned if `outdir` is None. alignarray: np.ndarray zalignarray: np.ndarray featurearray: np.ndarray """ score_note_array = ensure_notearray(score_data) perf_note_array = ensure_notearray(performance_data) valid_score_note_array_fields = [ "onset_beat", "duration_beat", "onset_quarter", "duration_quarter", "onset_div", "duration_div", "pitch", "voice", "id", ] valid_perf_note_array_fields = [ "onset_sec", "duration_sec", "pitch", "velocity", "track", "channel", "id", ] ffields = [ ("velocity", "<f4"), ("timing", "<f4"), ("articulation", "<f4"), ("id", "U256"), ] farray = [] notes = list(score_note_array["id"]) if feature is not None: # veloctiy, timing, articulation, note for no, i in enumerate(list(feature["id"])): farray.append( ( feature["velocity"][no], feature["timing"][no], feature["articulation"][no], i, ) ) else: for no, i in enumerate(notes): farray.append((0, 0, 0, i)) featurearray = np.array(farray, dtype=ffields) alignarray = alignment_dicts_to_array(alignment) if zalign is not None: zalignarray = alignment_dicts_to_array(zalign) else: # if no zalign is available, save the same alignment twice zalignarray = alignment_dicts_to_array(alignment) if outdir is not None: np.savetxt( os.path.join(outdir, "ppart.csv"), # outdir + os.path.sep + "perf_note_array.csv", perf_note_array[valid_perf_note_array_fields], fmt="%.20s", delimiter=",", header=",".join(valid_perf_note_array_fields), comments="", ) np.savetxt( os.path.join(outdir, "part.csv"), # outdir + os.path.sep + "score_note_array.csv", score_note_array[valid_score_note_array_fields], fmt="%.20s", delimiter=",", header=",".join(valid_score_note_array_fields), comments="", ) np.savetxt( os.path.join(outdir, "align.csv"), # outdir + os.path.sep + "align.csv", alignarray, fmt="%.20s", delimiter=",", header=",".join(alignarray.dtype.names), comments="", ) np.savetxt( os.path.join(outdir, "zalign.csv"), # outdir + os.path.sep + "zalign.csv", zalignarray, fmt="%.20s", delimiter=",", header=",".join(zalignarray.dtype.names), comments="", ) np.savetxt( os.path.join(outdir, "feature.csv"), # outdir + os.path.sep + "feature.csv", featurearray, fmt="%.20s", delimiter=",", header=",".join(featurearray.dtype.names), comments="", ) else: return ( perf_note_array, score_note_array, alignarray, zalignarray, featurearray, )
# alias save_csv_for_parangonada = save_parangonada_csv @deprecated_alias(align="alignment", outfile="out") def save_parangonada_alignment( alignment: List[dict], out: Optional[PathLike] = None, ): """ Save only an alignment csv for visualization with parangonda. For score, performance, and expressive features use save_csv_for_parangonada() Parameters ---------- align : list A list of note alignment dictionaries. outdir : str A directory to save the files into. Returns ------- alignarray : np.ndarray Array containing the alignment. This array will only be returned if `out` is not None """ alignarray = alignment_dicts_to_array(alignment) if out is not None: np.savetxt( out, alignarray, fmt="%.20s", delimiter=",", header=",".join(alignarray.dtype.names), comments="", ) else: return alignarray # alias save_alignment_for_parangonada = save_parangonada_alignment @deprecated_alias(outfile="out", ppart="performance_data") def save_alignment_for_ASAP( alignment: List[dict], performance_data: PerformanceLike, out: PathLike, ) -> None: """ load an alignment exported from parangonda. Parameters ---------- alignment : list A list of note alignment dictionaries. performance_data : PerformanceLike A performance. out : str A path for the alignment tsv file. """ if isinstance(performance_data, (Performance, Iterable)): ppart = performance_data[0] elif isinstance(performance_data, PerformedPart): ppart = performance_data notes_indexed_by_id = { str(n["id"]): [ str(n["id"]), str(n["track"]), str(n["channel"]), str(n["midi_pitch"]), str(n["note_on"]), ] for n in ppart.notes } with open(out, "w") as f: f.write("xml_id\tmidi_id\ttrack\tchannel\tpitch\tonset\n") for line in alignment: if line["label"] == "match": outline_score = [str(line["score_id"])] outline_perf = notes_indexed_by_id[str(line["performance_id"])] f.write("\t".join(outline_score + outline_perf) + "\n") elif line["label"] == "deletion": outline_score = str(line["score_id"]) f.write(outline_score + "\tdeletion\n") elif line["label"] == "insertion": outline_score = ["insertion"] outline_perf = notes_indexed_by_id[str(line["performance_id"])] f.write("\t".join(outline_score + outline_perf) + "\n")