Parangonar
==========
**Parangonar** is a Python package for note alignment of symbolic music.
**Parangonar** uses [Partitura](https://github.com/CPJKU/partitura) as file I/O utility.
Note alignments produced py **Parangonar** can be visualized using the
web tool [Parangonda](https://sildater.github.io/parangonada/)
Installation
-------
The easiest way to install the package is via `pip` from the [PyPI (Python
Package Index)](https://pypi.python.org/pypi>):
```shell
pip install parangonar
```
This will install the latest release of the package and will install all dependencies automatically.
Getting Started
==========
The following code snippets load the contents of a a previously aligned performance
and score alignment file (encoded in the [match file format](https://cpjku.github.io/matchfile/)).
A new alignment is computed using different note matchers and the predicted alignment are compared to the ground truth:
For an interactive version of these snippets, check the `getting_started.ipynb` notebook.
1 - Automatic Note Matching: `AutomaticNoteMatcher` and `DualDTWNoteMatcher`
-----
```python
import parangonar as pa
import partitura as pt
perf_match, groundtruth_alignment, score_match = pt.load_match(
filename= pa.EXAMPLE,
create_score=True
)
# compute note arrays from the loaded score and performance
pna_match = perf_match[0].note_array()
sna_match = score_match[0].note_array()
# match the notes in the note arrays --------------------- DualDTWNoteMatcher
sdm = pa.AutomaticNoteMatcher()
pred_alignment = sdm(sna_match,
pna_match,
verbose_time=True)
# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
precision, recall, f_score = pa.fscore_alignments(pred_alignment,
groundtruth_alignment,
alignment_type)
print('Evaluate ',alignment_type)
print('Precision: ',format(precision, '.3f'),
'Recall ',format(recall, '.3f'),
'F-Score ',format(f_score, '.3f'))
print('------------------')
# this matcher requires grace note info
sna_match = score_match[0].note_array(include_grace_notes=True)
# match the notes in the note arrays --------------------- DualDTWNoteMatcher
sdm = pa.DualDTWNoteMatcher()
pred_alignment = sdm(sna_match,
pna_match,
process_ornaments=False,
score_part=score_match[0]) # if a score part is passed, ornaments can be handled seperately
# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
precision, recall, f_score = pa.fscore_alignments(pred_alignment,
groundtruth_alignment,
alignment_type)
print('Evaluate ',alignment_type)
print('Precision: ',format(precision, '.3f'),
'Recall ',format(recall, '.3f'),
'F-Score ',format(f_score, '.3f'))
print('------------------')
```
Aligning MusicXML Scores and MIDI Performances
-----
```python
import parangonar as pa
import partitura as pt
score = pt.load_score(filename= 'path/to/score_file')
performance = pt.load_performance_midi(filename= 'path/to/midi_file')
# compute note arrays from the loaded score and performance
pna = performance.note_array()
sna = score.note_array()
# match the notes in the note arrays
sdm = pa.AutomaticNoteMatcher()
pred_alignment = sdm(sna, pna)
```
2 - Anchor Point Alignment: `AnchorPointNoteMatcher`
----
```python
import parangonar as pa
import partitura as pt
perf_match, groundtruth_alignment, score_match = pt.load_match(
filename= pa.EXAMPLE,
create_score=True
)
# compute note arrays from the loaded score and performance
pna_match = perf_match.note_array()
sna_match = score_match.note_array()
# compute synthetic anchor points every 4 beats
nodes = pa.match.node_array(score_match[0],
perf_match[0],
groundtruth_alignment,
node_interval=4)
# match the notes in the note arrays
apdm = pa.AnchorPointNoteMatcher()
pred_alignment = apdm(sna_match,
pna_match,
nodes)
# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
precision, recall, f_score = pa.fscore_alignments(pred_alignment,
groundtruth_alignment,
alignment_type)
print('Evaluate ',alignment_type)
print('Precision: ',format(precision, '.3f'),
'Recall ',format(recall, '.3f'),
'F-Score ',format(f_score, '.3f'))
print('------------------')
```
3 - Online / Realtime Alignment: `OnlineTransformerMatcher` and `OnlinePureTransformerMatcher`
----
```python
import parangonar as pa
import partitura as pt
perf_match, groundtruth_alignment, score_match = pt.load_match(
filename= pa.EXAMPLE,
create_score=True
)
# compute note arrays from the loaded score and performance
pna_match = perf_match[0].note_array()
# this matcher requires grace note info
sna_match = score_match[0].note_array(include_grace_notes=True)
# set up the matcher using the score information: OnlineTransformerMatcher / OnlinePureTransformerMatcher
matcher = pa.OnlinePureTransformerMatcher(sna_match)
# the "offline" method loops over all notes in the performance and calls the "online" method for each one.
pred_alignment = matcher.offline(pna_match)
# compute f-score and print the results
print('------------------')
types = ['match','insertion', 'deletion']
for alignment_type in types:
precision, recall, f_score = pa.fscore_alignments(pred_alignment,
groundtruth_alignment,
alignment_type)
print('Evaluate ',alignment_type)
print('Precision: ',format(precision, '.3f'),
'Recall ',format(recall, '.3f'),
'F-Score ',format(f_score, '.3f'))
print('------------------')
```
4 - Visualize Alignment
----
```python
import parangonar as pa
import partitura as pt
perf_match, alignment, score_match = pt.load_match(
filename= pa.EXAMPLE,
create_score=True
)
pna_match = perf_match.note_array()
sna_match = score_match.note_array()
# show or save plot of note alignment
pa.plot_alignment(pna_match,
sna_match,
alignment,s
save_file = False)
# or plot the performance and score as piano rolls given a reference:
# we can encode errors if given ground truth
# Blue lines indicate correct matches, red lines incorrect ones.
pa.plot_alignment_comparison(pna_match, sna_match,
pred_alignment, groundtruth_alignment)
```
5 - File I/O for note alignments
----
Most I/O functions are handled by partitura.
For [Parangonada](https://sildater.github.io/parangonada/):
- pt.io.importparangonada.load_parangonada_alignment
- pt.io.importparangonada.load_parangonada_csv
- pt.io.exportparangonada.save_parangonada_alignment
- pt.io.exportparangonada.save_parangonada_csv
For [(n)ASAP alignments](https://github.com/CPJKU/asap-dataset)
- pt.io.importparangonada.load_alignment_from_ASAP
- pt.io.exportparangonada.save_alignment_for_ASAP
For [match files](https://cpjku.github.io/matchfile/)
- pt.io.importmatch.load_match
- pt.io.exportmatch.save_match
and a basic interface for saving parangonada-ready csv files is also available:
----
```python
import partitura as pt
import parangonar as pa
# export a note alignment for visualization with parangonada:
# https://sildater.github.io/parangonada/
pa.match.save_parangonada_csv(alignment,
performance_data,
score_data,
outdir="path/to/dir")
# import a corrected note alignment from parangonada:
# https://sildater.github.io/parangonada/
alignment = pt.io.importparangonada.load_parangonada_alignment(filename= 'path/to/note_alignment.csv')
# load note alignments of the asap dataset:
# https://github.com/CPJKU/asap-dataset/tree/note_alignments
alignment = pt.io.importparangonada.load_alignment_from_ASAP(filename= 'path/to/note_alignment.tsv')
```
6 - Aligned Data
----
These note-aligned datasets are publically available:
- [Vienna 4x22](https://github.com/CPJKU/vienna4x22)
- [(n)ASAP note alignments](https://github.com/CPJKU/asap-dataset)
- [Batik Dataset](https://github.com/huispaty/batik_plays_mozart)
Publications
=====
Two publications are associated with models available in **Parangonar**.
The anchor point-enhanced `AnchorPointNoteMatcher` and the automatic `AutomaticNoteMatcher` are this described in:
```
@article{nasap-dataset,
title = {Automatic Note-Level Score-to-Performance Alignments in the ASAP Dataset},
author = {Peter, Silvan David and Cancino-Chacón, Carlos Eduardo and Foscarin, Francesco and McLeod, Andrew Philip and Henkel, Florian and Karystinaios, Emmanouil and Widmer, Gerhard},
doi = {10.5334/tismir.149},
journal = {Transactions of the International Society for Music Information Retrieval {(TISMIR)}},
year = {2023}
}
```
and the former is used in the creation of the [note-aligned (n)ASAP Dataset](https://github.com/CPJKU/asap-dataset).
The improved automatic `DualDTWNoteMatcher` and the online / realtime `OnlineTransformerMatcher` / `OnlinePureTransformerMatcher` are described in:
```
@inproceedings{peter-2023,
title={Online Symbolic Music Alignment with Offline Reinforcement Learning},
author={Peter, Silvan David},
booktitle={International Society for Music Information Retrieval Conference {(ISMIR)}},
year={2023}
}
```
Acknowledgments
=======
This work is supported by the European Research Council (ERC) under the EU’s Horizon 2020 research & innovation programme, grant agreement No. 10101937 (”Wither Music?”).
License
=======
The code in this package is licensed under the Apache 2.0 License. For details,
please see the [LICENSE](LICENSE) file.
Raw data
{
"_id": null,
"home_page": "https://github.com/sildater/parangonar",
"name": "parangonar",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "match alignment midi performance score",
"author": "Silvan Peter, Carlos Cancino-Chac\u00f3n, Florian Henkel",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/4d/a6/1d49348b66c6fff938f9f934b00fd8531959b595d74e80915ada28341341/parangonar-1.0.0.tar.gz",
"platform": null,
"description": "\r\nParangonar\r\n==========\r\n\r\n**Parangonar** is a Python package for note alignment of symbolic music. \r\n**Parangonar** uses [Partitura](https://github.com/CPJKU/partitura) as file I/O utility.\r\nNote alignments produced py **Parangonar** can be visualized using the \r\nweb tool [Parangonda](https://sildater.github.io/parangonada/)\r\n\r\n\r\nInstallation\r\n-------\r\n\r\nThe easiest way to install the package is via `pip` from the [PyPI (Python\r\nPackage Index)](https://pypi.python.org/pypi>):\r\n```shell\r\npip install parangonar\r\n```\r\nThis will install the latest release of the package and will install all dependencies automatically.\r\n\r\n\r\nGetting Started\r\n==========\r\n\r\nThe following code snippets load the contents of a a previously aligned performance\r\nand score alignment file (encoded in the [match file format](https://cpjku.github.io/matchfile/)). \r\n\r\nA new alignment is computed using different note matchers and the predicted alignment are compared to the ground truth:\r\n\r\nFor an interactive version of these snippets, check the `getting_started.ipynb` notebook.\r\n\r\n\r\n1 - Automatic Note Matching: `AutomaticNoteMatcher` and `DualDTWNoteMatcher`\r\n-----\r\n\r\n```python\r\nimport parangonar as pa\r\nimport partitura as pt\r\n\r\nperf_match, groundtruth_alignment, score_match = pt.load_match(\r\n filename= pa.EXAMPLE,\r\n create_score=True\r\n)\r\n\r\n# compute note arrays from the loaded score and performance\r\npna_match = perf_match[0].note_array()\r\nsna_match = score_match[0].note_array()\r\n\r\n# match the notes in the note arrays --------------------- DualDTWNoteMatcher\r\nsdm = pa.AutomaticNoteMatcher()\r\npred_alignment = sdm(sna_match, \r\n pna_match,\r\n verbose_time=True)\r\n\r\n# compute f-score and print the results\r\nprint('------------------')\r\ntypes = ['match','insertion', 'deletion']\r\nfor alignment_type in types:\r\n precision, recall, f_score = pa.fscore_alignments(pred_alignment, \r\n groundtruth_alignment, \r\n alignment_type)\r\n print('Evaluate ',alignment_type)\r\n print('Precision: ',format(precision, '.3f'),\r\n 'Recall ',format(recall, '.3f'),\r\n 'F-Score ',format(f_score, '.3f'))\r\n print('------------------')\r\n\r\n\r\n\r\n\r\n# this matcher requires grace note info\r\nsna_match = score_match[0].note_array(include_grace_notes=True)\r\n\r\n# match the notes in the note arrays --------------------- DualDTWNoteMatcher\r\nsdm = pa.DualDTWNoteMatcher()\r\npred_alignment = sdm(sna_match, \r\n pna_match,\r\n process_ornaments=False,\r\n score_part=score_match[0]) # if a score part is passed, ornaments can be handled seperately\r\n\r\n# compute f-score and print the results\r\nprint('------------------')\r\ntypes = ['match','insertion', 'deletion']\r\nfor alignment_type in types:\r\n precision, recall, f_score = pa.fscore_alignments(pred_alignment, \r\n groundtruth_alignment, \r\n alignment_type)\r\n print('Evaluate ',alignment_type)\r\n print('Precision: ',format(precision, '.3f'),\r\n 'Recall ',format(recall, '.3f'),\r\n 'F-Score ',format(f_score, '.3f'))\r\n print('------------------')\r\n\r\n```\r\n\r\nAligning MusicXML Scores and MIDI Performances\r\n-----\r\n\r\n```python\r\nimport parangonar as pa\r\nimport partitura as pt\r\n\r\nscore = pt.load_score(filename= 'path/to/score_file')\r\nperformance = pt.load_performance_midi(filename= 'path/to/midi_file')\r\n\r\n# compute note arrays from the loaded score and performance\r\npna = performance.note_array()\r\nsna = score.note_array()\r\n\r\n# match the notes in the note arrays\r\nsdm = pa.AutomaticNoteMatcher()\r\npred_alignment = sdm(sna, pna)\r\n\r\n```\r\n\r\n2 - Anchor Point Alignment: `AnchorPointNoteMatcher` \r\n----\r\n\r\n```python\r\nimport parangonar as pa\r\nimport partitura as pt\r\n\r\nperf_match, groundtruth_alignment, score_match = pt.load_match(\r\n filename= pa.EXAMPLE,\r\n create_score=True\r\n)\r\n\r\n# compute note arrays from the loaded score and performance\r\npna_match = perf_match.note_array()\r\nsna_match = score_match.note_array()\r\n\r\n# compute synthetic anchor points every 4 beats\r\nnodes = pa.match.node_array(score_match[0], \r\n perf_match[0], \r\n groundtruth_alignment,\r\n node_interval=4)\r\n\r\n# match the notes in the note arrays\r\napdm = pa.AnchorPointNoteMatcher()\r\npred_alignment = apdm(sna_match, \r\n pna_match,\r\n nodes)\r\n\r\n# compute f-score and print the results\r\nprint('------------------')\r\ntypes = ['match','insertion', 'deletion']\r\nfor alignment_type in types:\r\n precision, recall, f_score = pa.fscore_alignments(pred_alignment, \r\n groundtruth_alignment, \r\n alignment_type)\r\n print('Evaluate ',alignment_type)\r\n print('Precision: ',format(precision, '.3f'),\r\n 'Recall ',format(recall, '.3f'),\r\n 'F-Score ',format(f_score, '.3f'))\r\n print('------------------')\r\n```\r\n\r\n\r\n3 - Online / Realtime Alignment: `OnlineTransformerMatcher` and `OnlinePureTransformerMatcher` \r\n----\r\n\r\n```python\r\nimport parangonar as pa\r\nimport partitura as pt\r\n\r\nperf_match, groundtruth_alignment, score_match = pt.load_match(\r\n filename= pa.EXAMPLE,\r\n create_score=True\r\n)\r\n\r\n# compute note arrays from the loaded score and performance\r\npna_match = perf_match[0].note_array()\r\n# this matcher requires grace note info\r\nsna_match = score_match[0].note_array(include_grace_notes=True)\r\n\r\n# set up the matcher using the score information: OnlineTransformerMatcher / OnlinePureTransformerMatcher\r\nmatcher = pa.OnlinePureTransformerMatcher(sna_match)\r\n\r\n# the \"offline\" method loops over all notes in the performance and calls the \"online\" method for each one.\r\npred_alignment = matcher.offline(pna_match)\r\n\r\n# compute f-score and print the results\r\nprint('------------------')\r\ntypes = ['match','insertion', 'deletion']\r\nfor alignment_type in types:\r\n precision, recall, f_score = pa.fscore_alignments(pred_alignment, \r\n groundtruth_alignment, \r\n alignment_type)\r\n print('Evaluate ',alignment_type)\r\n print('Precision: ',format(precision, '.3f'),\r\n 'Recall ',format(recall, '.3f'),\r\n 'F-Score ',format(f_score, '.3f'))\r\n print('------------------')\r\n```\r\n\r\n4 - Visualize Alignment\r\n----\r\n\r\n```python\r\nimport parangonar as pa\r\nimport partitura as pt\r\n\r\nperf_match, alignment, score_match = pt.load_match(\r\n filename= pa.EXAMPLE,\r\n create_score=True\r\n)\r\npna_match = perf_match.note_array()\r\nsna_match = score_match.note_array()\r\n\r\n# show or save plot of note alignment\r\npa.plot_alignment(pna_match,\r\n sna_match,\r\n alignment,s\r\n save_file = False)\r\n\r\n# or plot the performance and score as piano rolls given a reference: \r\n# we can encode errors if given ground truth\r\n# Blue lines indicate correct matches, red lines incorrect ones.\r\npa.plot_alignment_comparison(pna_match, sna_match, \r\n pred_alignment, groundtruth_alignment)\r\n```\r\n\r\n5 - File I/O for note alignments\r\n----\r\n\r\nMost I/O functions are handled by partitura. \r\nFor [Parangonada](https://sildater.github.io/parangonada/):\r\n- pt.io.importparangonada.load_parangonada_alignment\r\n- pt.io.importparangonada.load_parangonada_csv\r\n- pt.io.exportparangonada.save_parangonada_alignment\r\n- pt.io.exportparangonada.save_parangonada_csv\r\n\r\nFor [(n)ASAP alignments](https://github.com/CPJKU/asap-dataset)\r\n- pt.io.importparangonada.load_alignment_from_ASAP\r\n- pt.io.exportparangonada.save_alignment_for_ASAP\r\n\r\nFor [match files](https://cpjku.github.io/matchfile/)\r\n- pt.io.importmatch.load_match\r\n- pt.io.exportmatch.save_match\r\n\r\nand a basic interface for saving parangonada-ready csv files is also available:\r\n\r\n----\r\n\r\n```python\r\nimport partitura as pt\r\nimport parangonar as pa\r\n\r\n# export a note alignment for visualization with parangonada:\r\n# https://sildater.github.io/parangonada/\r\npa.match.save_parangonada_csv(alignment, \r\n performance_data,\r\n score_data,\r\n outdir=\"path/to/dir\")\r\n\r\n\r\n# import a corrected note alignment from parangonada:\r\n# https://sildater.github.io/parangonada/\r\nalignment = pt.io.importparangonada.load_parangonada_alignment(filename= 'path/to/note_alignment.csv')\r\n\r\n# load note alignments of the asap dataset: \r\n# https://github.com/CPJKU/asap-dataset/tree/note_alignments\r\nalignment = pt.io.importparangonada.load_alignment_from_ASAP(filename= 'path/to/note_alignment.tsv')\r\n```\r\n\r\n\r\n6 - Aligned Data\r\n----\r\n\r\nThese note-aligned datasets are publically available:\r\n- [Vienna 4x22](https://github.com/CPJKU/vienna4x22)\r\n- [(n)ASAP note alignments](https://github.com/CPJKU/asap-dataset)\r\n- [Batik Dataset](https://github.com/huispaty/batik_plays_mozart)\r\n\r\nPublications\r\n=====\r\n\r\nTwo publications are associated with models available in **Parangonar**.\r\nThe anchor point-enhanced `AnchorPointNoteMatcher` and the automatic `AutomaticNoteMatcher` are this described in:\r\n\r\n```\r\n@article{nasap-dataset,\r\n title = {Automatic Note-Level Score-to-Performance Alignments in the ASAP Dataset},\r\n author = {Peter, Silvan David and Cancino-Chac\u00f3n, Carlos Eduardo and Foscarin, Francesco and McLeod, Andrew Philip and Henkel, Florian and Karystinaios, Emmanouil and Widmer, Gerhard},\r\n doi = {10.5334/tismir.149},\r\n journal = {Transactions of the International Society for Music Information Retrieval {(TISMIR)}},\r\n year = {2023}\r\n}\r\n```\r\n\r\nand the former is used in the creation of the [note-aligned (n)ASAP Dataset](https://github.com/CPJKU/asap-dataset).\r\n\r\n\r\nThe improved automatic `DualDTWNoteMatcher` and the online / realtime `OnlineTransformerMatcher` / `OnlinePureTransformerMatcher` are described in:\r\n\r\n\r\n```\r\n@inproceedings{peter-2023,\r\n title={Online Symbolic Music Alignment with Offline Reinforcement Learning},\r\n author={Peter, Silvan David},\r\n booktitle={International Society for Music Information Retrieval Conference {(ISMIR)}},\r\n year={2023}\r\n}\r\n```\r\n\r\nAcknowledgments\r\n=======\r\n\r\nThis work is supported by the European Research Council (ERC) under the EU\u2019s Horizon 2020 research & innovation programme, grant agreement No. 10101937 (\u201dWither Music?\u201d).\r\n\r\nLicense\r\n=======\r\n\r\nThe code in this package is licensed under the Apache 2.0 License. For details,\r\nplease see the [LICENSE](LICENSE) file.\r\n",
"bugtrack_url": null,
"license": "Apache 2.0",
"summary": "Symbolic music alignment",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/sildater/parangonar"
},
"split_keywords": [
"match",
"alignment",
"midi",
"performance",
"score"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b9481580b4fa5f89410a8ad64df8fe5744a4f99911d8b6952ba99798a3152b59",
"md5": "60c1e1dbdf35cf6ce550a4fc60e8150a",
"sha256": "6f117a1ba71bd63cd4232cf4287d3a50217f6f5c99409885b8103fed48bf560d"
},
"downloads": -1,
"filename": "parangonar-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "60c1e1dbdf35cf6ce550a4fc60e8150a",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7",
"size": 42776,
"upload_time": "2023-11-02T19:47:52",
"upload_time_iso_8601": "2023-11-02T19:47:52.690643Z",
"url": "https://files.pythonhosted.org/packages/b9/48/1580b4fa5f89410a8ad64df8fe5744a4f99911d8b6952ba99798a3152b59/parangonar-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "4da61d49348b66c6fff938f9f934b00fd8531959b595d74e80915ada28341341",
"md5": "ea009387752a6bfdde5bc80ac6532047",
"sha256": "350dcc2d5718c6c2937e049ec94f81e48a47fbdb6a38e2d5a38808a3bfd3980b"
},
"downloads": -1,
"filename": "parangonar-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "ea009387752a6bfdde5bc80ac6532047",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 43818,
"upload_time": "2023-11-02T19:47:54",
"upload_time_iso_8601": "2023-11-02T19:47:54.456562Z",
"url": "https://files.pythonhosted.org/packages/4d/a6/1d49348b66c6fff938f9f934b00fd8531959b595d74e80915ada28341341/parangonar-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-11-02 19:47:54",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "sildater",
"github_project": "parangonar",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "numpy",
"specs": []
},
{
"name": "scipy",
"specs": []
},
{
"name": "partitura",
"specs": [
[
"==",
"1.1.1"
]
]
},
{
"name": "torch",
"specs": []
}
],
"lcname": "parangonar"
}