"""Haydn Op.20 Dataset."""
from pathlib import Path
from typing import List, Union
import music21
from music21.roman import RomanNumeral
from ..classes import Annotation
from ..inputs import from_music21_score
from ..music import DEFAULT_RESOLUTION, Music
from .base import DatasetInfo, RemoteFolderDataset
# pylint: disable=line-too-long
_NAME = "Haydn Op.20 Dataset"
_DESCRIPTION = """\
This dataset is a set of functional harmonic analysis annotations \
for the Op.20 string quartets from Joseph Haydn, commonly known as \
the 'Sun' quartets."""
_HOMEPAGE = "https://doi.org/10.5281/zenodo.1095630"
_CITATION = """\
@dataset{nestor_napoles_lopez_2017_1095630,
author={N\'apoles L\'opez, N\'estor},
title={{Joseph Haydn - String Quartets Op.20 - Harmonic Analysis Annotations Dataset}},
month=dec,
year=2017,
publisher={Zenodo},
version={v1.1-alpha},
doi={10.5281/zenodo.1095630},
url={https://doi.org/10.5281/zenodo.1095630}
}"""
[docs]class HaydnOp20Dataset(RemoteFolderDataset):
"""Haydn Op.20 Dataset."""
_info = DatasetInfo(_NAME, _DESCRIPTION, _HOMEPAGE)
_citation = _CITATION
_sources = {
"haydn": {
"filename": "haydnop20v1.3_annotated.zip",
"url": "https://github.com/napulen/haydn_op20_harm/releases/download/v1.3/haydnop20v1.3_annotated.zip",
"archive": True,
"size": 130954,
"md5": "1c65c8da312e1c9dda681d0496bf527f",
"sha256": "96986cccebfd37a36cc97a2fc0ebcfbe22d5136e622b21e04ea125d589f5073b",
}
}
_extension = "hrm"
[docs] def read(self, filename: Union[str, Path]) -> Music:
"""Read a file into a Music object."""
score = music21.converter.parse(filename, format="humdrum")
# Get the annotations
roman_numerals = list(score.flat.getElementsByClass("RomanNumeral"))
annotations = get_annotations(roman_numerals)
# Remove the annotations from the original score
# (they mess with the python representation)
score.remove(roman_numerals, recurse=True)
music = from_music21_score(score)
music.annotations = annotations
return music
def get_annotations(
roman_numerals: List[RomanNumeral], resolution=DEFAULT_RESOLUTION
) -> List[Annotation]:
"""Return music21 RomanNumeral objects as Annotation objects."""
# Convert the list into a dictionary to remove duplicate items
roman_numeral_dict = {rn.offset: rn for rn in roman_numerals if rn}
annotations = []
for offset, roman_numeral in roman_numeral_dict.items():
time = int(round(float(offset * resolution)))
tonicized_key = roman_numeral.secondaryRomanNumeralKey
key = tonicized_key if tonicized_key else roman_numeral.key
annotation = {
"key": key.tonicPitchNameWithCase,
"figure": roman_numeral.figure,
"chord": roman_numeral.pitchedCommonName,
}
annotations.append(Annotation(time=time, annotation=annotation))
return annotations