partitura 1.6.0__tar.gz → 1.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {partitura-1.6.0 → partitura-1.8.0}/PKG-INFO +34 -19
- partitura-1.8.0/docs/source/conf.py +120 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/__init__.py +8 -9
- {partitura-1.6.0 → partitura-1.8.0}/partitura/assets/score_example.musicxml +3 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/directions.py +12 -2
- {partitura-1.6.0 → partitura-1.8.0}/partitura/display.py +4 -4
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/__init__.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportaudio.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportkern.py +6 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportmatch.py +36 -26
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportmidi.py +28 -15
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportmusicxml.py +58 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importdcml.py +9 -4
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importmatch.py +45 -20
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importmei.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importmidi.py +33 -31
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importmusicxml.py +104 -27
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importparangonada.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/matchfile_base.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/matchfile_utils.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/matchlines_v0.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/matchlines_v1.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/__init__.py +0 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/key_identification.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/note_features.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/performance_codec.py +52 -21
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/performance_features.py +24 -4
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/pitch_spelling.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/voice_separation.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/performance.py +37 -16
- {partitura-1.6.0 → partitura-1.8.0}/partitura/score.py +255 -92
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/__init__.py +0 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/generic.py +55 -62
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/globals.py +0 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/misc.py +3 -2
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/music.py +323 -90
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/normalize.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/synth.py +1 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura.egg-info/PKG-INFO +34 -19
- {partitura-1.6.0 → partitura-1.8.0}/partitura.egg-info/SOURCES.txt +3 -2
- partitura-1.8.0/partitura.egg-info/requires.txt +31 -0
- partitura-1.8.0/partitura.egg-info/top_level.txt +4 -0
- partitura-1.8.0/pyproject.toml +92 -0
- partitura-1.8.0/tests/test_harmony.py +387 -0
- partitura-1.8.0/tests/test_iter.py +164 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_midi_export.py +10 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_midi_import.py +136 -1
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_note_features.py +14 -0
- partitura-1.8.0/tests/test_part_properties.py +103 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_performance.py +1 -1
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_pianoroll.py +2 -1
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_synth.py +2 -2
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_times.py +4 -7
- partitura-1.8.0/tests/test_transpose.py +43 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_utils.py +65 -1
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_xml.py +25 -12
- partitura-1.6.0/partitura.egg-info/requires.txt +0 -6
- partitura-1.6.0/partitura.egg-info/top_level.txt +0 -1
- partitura-1.6.0/setup.py +0 -79
- partitura-1.6.0/tests/test_harmony.py +0 -31
- partitura-1.6.0/tests/test_part_properties.py +0 -38
- partitura-1.6.0/tests/test_quarter_adjust.py +0 -157
- partitura-1.6.0/tests/test_transpose.py +0 -26
- {partitura-1.6.0 → partitura-1.8.0}/LICENSE +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/README.md +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/assets/musicxml.xsd +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/assets/score_example.krn +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/assets/score_example.mei +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/assets/score_example.mid +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportmei.py +1 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/exportparangonada.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importkern.py +1 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importmusic21.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/importnakamura.py +1 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/io/musescore.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/meter.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/note_array_to_score.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura/musicanalysis/tonal_tension.py +1 -1
- {partitura-1.6.0 → partitura-1.8.0}/partitura/utils/fluidsynth.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/partitura.egg-info/dependency_links.txt +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/setup.cfg +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_clef.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_cross_staff.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_dcml_import.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_deprecations.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_display.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_fluidsynth.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_kern.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_key_estimation.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_load_performance.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_load_score.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_m21_import.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_match_export.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_match_import.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_mei.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_merge_parts.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_metrical_position.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_musescore.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_nakamura.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_new_divs.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_note_array.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_octave_shift.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_parangonada.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_partial_measures.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_performance_codec.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_performance_features.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_pitch_spelling.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_rest_array.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_time_estimation.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_tonal_tension.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_urlload.py +0 -0
- {partitura-1.6.0 → partitura-1.8.0}/tests/test_voice_estimation.py +0 -0
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: partitura
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
4
4
|
Summary: A package for handling symbolic musical information
|
|
5
|
-
Home-page: https://github.com/CPJKU/partitura
|
|
6
5
|
Author: Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier, Patricia Hu
|
|
7
|
-
|
|
6
|
+
Maintainer-email: partitura-users@googlegroups.com
|
|
8
7
|
License: Apache 2.0
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Project-URL: Homepage, https://github.com/CPJKU/partitura
|
|
9
|
+
Keywords: music,notation,musicxml,midi
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
11
|
Classifier: Programming Language :: Python
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
16
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
17
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
15
|
-
Requires-Python: >=3.
|
|
18
|
+
Requires-Python: >=3.10
|
|
16
19
|
Description-Content-Type: text/markdown
|
|
17
20
|
License-File: LICENSE
|
|
18
21
|
Requires-Dist: numpy
|
|
@@ -21,18 +24,30 @@ Requires-Dist: lxml
|
|
|
21
24
|
Requires-Dist: lark-parser
|
|
22
25
|
Requires-Dist: xmlschema
|
|
23
26
|
Requires-Dist: mido
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
Requires-Dist: typing-extensions
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest; extra == "dev"
|
|
30
|
+
Requires-Dist: black; extra == "dev"
|
|
31
|
+
Requires-Dist: nbsphinx; extra == "dev"
|
|
32
|
+
Requires-Dist: music21; extra == "dev"
|
|
33
|
+
Requires-Dist: Pillow; extra == "dev"
|
|
34
|
+
Requires-Dist: musescore; extra == "dev"
|
|
35
|
+
Requires-Dist: miditok; extra == "dev"
|
|
36
|
+
Requires-Dist: tokenizers; extra == "dev"
|
|
37
|
+
Requires-Dist: pandas; extra == "dev"
|
|
38
|
+
Provides-Extra: all
|
|
39
|
+
Requires-Dist: nbsphinx; extra == "all"
|
|
40
|
+
Requires-Dist: torch; extra == "all"
|
|
41
|
+
Requires-Dist: pyfluidsynth; extra == "all"
|
|
42
|
+
Requires-Dist: pytest; extra == "all"
|
|
43
|
+
Requires-Dist: black; extra == "all"
|
|
44
|
+
Requires-Dist: music21; extra == "all"
|
|
45
|
+
Requires-Dist: Pillow; extra == "all"
|
|
46
|
+
Requires-Dist: musescore; extra == "all"
|
|
47
|
+
Requires-Dist: miditok; extra == "all"
|
|
48
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
49
|
+
Requires-Dist: pandas; extra == "all"
|
|
50
|
+
Dynamic: license-file
|
|
36
51
|
|
|
37
52
|
[//]: # (<p align="center"> )
|
|
38
53
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Configuration file for the Sphinx documentation builder.
|
|
2
|
+
#
|
|
3
|
+
# This file only contains a selection of the most common options. For a full
|
|
4
|
+
# list see the documentation:
|
|
5
|
+
# http://www.sphinx-doc.org/en/master/config
|
|
6
|
+
|
|
7
|
+
# -- Path setup --------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
# If extensions (or modules to document with autodoc) are in another directory,
|
|
10
|
+
# add these directories to sys.path here. If the directory is relative to the
|
|
11
|
+
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
12
|
+
#
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
sys.path.insert(0, os.path.abspath("../../partitura"))
|
|
17
|
+
# The master toctree document.
|
|
18
|
+
master_doc = "index"
|
|
19
|
+
|
|
20
|
+
# -- Project information -----------------------------------------------------
|
|
21
|
+
|
|
22
|
+
project = "partitura"
|
|
23
|
+
# copyright = '2019, Maarten Grachten'
|
|
24
|
+
author = "Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier"
|
|
25
|
+
|
|
26
|
+
# The version info for the project you're documenting, acts as replacement for
|
|
27
|
+
# |version| and |release|, also used in various other places throughout the
|
|
28
|
+
# built documents.
|
|
29
|
+
#
|
|
30
|
+
# The short X.Y version.
|
|
31
|
+
version = "1.8.0"
|
|
32
|
+
# The full version, including alpha/beta/rc tags.
|
|
33
|
+
release = "1.8.0"
|
|
34
|
+
|
|
35
|
+
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
36
|
+
# for a list of supported languages.
|
|
37
|
+
#
|
|
38
|
+
# This is also used if you do content translation via gettext catalogs.
|
|
39
|
+
# Usually you set "language" from the command line for these cases.
|
|
40
|
+
language = "python"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# -- General configuration ---------------------------------------------------
|
|
44
|
+
|
|
45
|
+
# List of patterns, relative to source directory, that match files and
|
|
46
|
+
# directories to ignore when looking for source files.
|
|
47
|
+
exclude_patterns = ["_build"]
|
|
48
|
+
|
|
49
|
+
# The name of the Pygments (syntax highlighting) style to use.
|
|
50
|
+
pygments_style = "sphinx"
|
|
51
|
+
|
|
52
|
+
# Add any Sphinx extension module names here, as strings. They can be
|
|
53
|
+
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
|
54
|
+
# ones.
|
|
55
|
+
extensions = [
|
|
56
|
+
"sphinx.ext.autodoc",
|
|
57
|
+
"sphinx.ext.autosummary",
|
|
58
|
+
"sphinx.ext.doctest",
|
|
59
|
+
"sphinx.ext.todo",
|
|
60
|
+
"sphinx.ext.coverage",
|
|
61
|
+
"sphinx.ext.mathjax",
|
|
62
|
+
"sphinx.ext.viewcode",
|
|
63
|
+
# 'sphinxcontrib.napoleon',
|
|
64
|
+
"sphinx.ext.napoleon",
|
|
65
|
+
"nbsphinx",
|
|
66
|
+
# 'sphinxcontrib.bibtex', # for bibliographic references
|
|
67
|
+
# 'sphinxcontrib.rsvgconverter', # for SVG->PDF conversion in LaTeX output
|
|
68
|
+
# 'sphinx_gallery.load_style', # load CSS for gallery (needs SG >= 0.6)
|
|
69
|
+
# 'sphinx_last_updated_by_git', #? get "last updated" from Git
|
|
70
|
+
# 'sphinx_codeautolink', # automatic links from code to documentation
|
|
71
|
+
# 'sphinx.ext.intersphinx', # links to other Sphinx projects (e.g. NumPy)
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
# These projects are also used for the sphinx_codeautolink extension:
|
|
75
|
+
intersphinx_mapping = {
|
|
76
|
+
"IPython": ("https://ipython.readthedocs.io/en/stable/", None),
|
|
77
|
+
"matplotlib": ("https://matplotlib.org/", None),
|
|
78
|
+
"numpy": ("https://docs.scipy.org/doc/numpy/", None),
|
|
79
|
+
"pandas": ("https://pandas.pydata.org/docs/", None),
|
|
80
|
+
"python": ("https://docs.python.org/3/", None),
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
# see http://stackoverflow.com/q/12206334/562769
|
|
84
|
+
numpydoc_show_class_members = False
|
|
85
|
+
|
|
86
|
+
# Add any paths that contain templates here, relative to this directory.
|
|
87
|
+
templates_path = ["_templates"]
|
|
88
|
+
|
|
89
|
+
# The suffix(es) of source filenames.
|
|
90
|
+
# You can specify multiple suffix as a list of string:
|
|
91
|
+
# source_suffix = ['.rst', '.md']
|
|
92
|
+
source_suffix = ".rst"
|
|
93
|
+
|
|
94
|
+
# List of patterns, relative to source directory, that match files and
|
|
95
|
+
# directories to ignore when looking for source files.
|
|
96
|
+
# This pattern also affects html_static_path and html_extra_path.
|
|
97
|
+
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
# -- Options for HTML output -------------------------------------------------
|
|
101
|
+
|
|
102
|
+
# The theme to use for HTML and HTML Help pages. See the documentation for
|
|
103
|
+
# a list of builtin themes.
|
|
104
|
+
#
|
|
105
|
+
# only import and set the theme if we're building docs locally
|
|
106
|
+
if os.environ.get("READTHEDOCS") != "True":
|
|
107
|
+
import sphinx_rtd_theme
|
|
108
|
+
|
|
109
|
+
html_theme = "sphinx_rtd_theme"
|
|
110
|
+
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
|
|
111
|
+
else:
|
|
112
|
+
html_theme = "default"
|
|
113
|
+
|
|
114
|
+
# Add any paths that contain custom static files (such as style sheets) here,
|
|
115
|
+
# relative to this directory. They are copied after the builtin static files,
|
|
116
|
+
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
117
|
+
# html_static_path = ['_static']
|
|
118
|
+
|
|
119
|
+
# Output file base name for HTML help builder.
|
|
120
|
+
htmlhelp_basename = "partituradoc"
|
|
@@ -6,7 +6,9 @@ data, display rendered scores, and functions to estimate pitch
|
|
|
6
6
|
spelling, voice assignment, and key signature.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import
|
|
9
|
+
import sys
|
|
10
|
+
from importlib.metadata import version
|
|
11
|
+
from importlib.resources import files
|
|
10
12
|
|
|
11
13
|
from .io import load_score, load_performance, load_score_as_part, lp
|
|
12
14
|
from .io.musescore import load_via_musescore
|
|
@@ -29,18 +31,15 @@ from .display import render
|
|
|
29
31
|
from . import musicanalysis
|
|
30
32
|
from .musicanalysis import make_note_features, compute_note_array, full_note_array
|
|
31
33
|
|
|
32
|
-
|
|
33
34
|
# define a version variable
|
|
34
|
-
__version__ =
|
|
35
|
+
__version__ = version("partitura")
|
|
35
36
|
|
|
36
37
|
#: An example MusicXML file for didactic purposes
|
|
37
|
-
EXAMPLE_MUSICXML =
|
|
38
|
-
"partitura", "assets/score_example.musicxml"
|
|
39
|
-
)
|
|
38
|
+
EXAMPLE_MUSICXML = str(files("partitura") / "assets" / "score_example.musicxml")
|
|
40
39
|
|
|
41
|
-
EXAMPLE_MIDI =
|
|
42
|
-
EXAMPLE_MEI =
|
|
43
|
-
EXAMPLE_KERN =
|
|
40
|
+
EXAMPLE_MIDI = str(files("partitura") / "assets" / "score_example.mid")
|
|
41
|
+
EXAMPLE_MEI = str(files("partitura") / "assets" / "score_example.mei")
|
|
42
|
+
EXAMPLE_KERN = str(files("partitura") / "assets" / "score_example.krn")
|
|
44
43
|
|
|
45
44
|
__all__ = [
|
|
46
45
|
"load_score",
|
|
@@ -327,6 +327,12 @@ def regularize_form(children):
|
|
|
327
327
|
return " ".join(unabbreviate(ch.lower()) for ch in children)
|
|
328
328
|
|
|
329
329
|
|
|
330
|
+
def check_tree(tree, col_name="column"):
|
|
331
|
+
meta = getattr(tree, "meta", None)
|
|
332
|
+
col = getattr(meta, col_name, None)
|
|
333
|
+
return col
|
|
334
|
+
|
|
335
|
+
|
|
330
336
|
def create_directions(tree, string, start=None, end=None):
|
|
331
337
|
"""
|
|
332
338
|
Recursively walk the parse tree of `string` to create a `score.Direction`
|
|
@@ -334,9 +340,13 @@ def create_directions(tree, string, start=None, end=None):
|
|
|
334
340
|
|
|
335
341
|
"""
|
|
336
342
|
if start is None:
|
|
337
|
-
|
|
343
|
+
col = check_tree(tree, "column")
|
|
344
|
+
if col is not None:
|
|
345
|
+
start = col - 1
|
|
338
346
|
if end is None:
|
|
339
|
-
|
|
347
|
+
col = check_tree(tree, "end_column")
|
|
348
|
+
if col is not None:
|
|
349
|
+
end = col - 1
|
|
340
350
|
|
|
341
351
|
if tree.data == "conj":
|
|
342
352
|
return create_directions(tree.children[0], string) + create_directions(
|
|
@@ -20,7 +20,6 @@ from partitura.score import ScoreLike
|
|
|
20
20
|
|
|
21
21
|
from partitura.utils.misc import PathLike, deprecated_alias
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
__all__ = ["render"]
|
|
25
24
|
|
|
26
25
|
# def ly_install_msg():
|
|
@@ -117,9 +116,10 @@ def render_lilypond(
|
|
|
117
116
|
|
|
118
117
|
prvw_sfx = ".preview.{}".format(fmt)
|
|
119
118
|
|
|
120
|
-
with
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
with (
|
|
120
|
+
TemporaryFile() as xml_fh,
|
|
121
|
+
NamedTemporaryFile(suffix=prvw_sfx, delete=False) as img_fh,
|
|
122
|
+
):
|
|
123
123
|
# save part to musicxml in file handle xml_fh
|
|
124
124
|
save_musicxml(score_data, xml_fh)
|
|
125
125
|
# rewind read pointer of file handle before we pass it to musicxml2ly
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
"""
|
|
4
4
|
This module contains methods for exporting Kern files.
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import math
|
|
7
8
|
from collections import defaultdict
|
|
9
|
+
from importlib.metadata import version
|
|
8
10
|
|
|
9
11
|
import numpy
|
|
10
12
|
|
|
@@ -19,6 +21,9 @@ from partitura.utils.misc import deprecated_alias, PathLike
|
|
|
19
21
|
__all__ = ["save_kern"]
|
|
20
22
|
|
|
21
23
|
|
|
24
|
+
VERSION = version("partitura")
|
|
25
|
+
|
|
26
|
+
|
|
22
27
|
ACC_TO_SIGN = {
|
|
23
28
|
0: "n",
|
|
24
29
|
-1: "-",
|
|
@@ -327,7 +332,7 @@ def save_kern(
|
|
|
327
332
|
out_data = exporter.parse()
|
|
328
333
|
out_data = exporter.trim(out_data)
|
|
329
334
|
# Use numpy savetxt to save the file
|
|
330
|
-
footer = "Encoded using the Partitura Python package, version
|
|
335
|
+
footer = f"Encoded using the Partitura Python package, version {VERSION}"
|
|
331
336
|
if out is not None:
|
|
332
337
|
np.savetxt(
|
|
333
338
|
fname=out,
|
|
@@ -7,6 +7,7 @@ Notes
|
|
|
7
7
|
-----
|
|
8
8
|
* The methods only export matchfiles version 1.0.0.
|
|
9
9
|
"""
|
|
10
|
+
|
|
10
11
|
import numpy as np
|
|
11
12
|
|
|
12
13
|
from typing import List, Optional, Iterable
|
|
@@ -70,7 +71,7 @@ def matchfile_from_alignment(
|
|
|
70
71
|
piece: Optional[str] = None,
|
|
71
72
|
score_filename: Optional[PathLike] = None,
|
|
72
73
|
performance_filename: Optional[PathLike] = None,
|
|
73
|
-
assume_part_unfolded: bool =
|
|
74
|
+
assume_part_unfolded: bool = True,
|
|
74
75
|
tempo_indication: Optional[str] = None,
|
|
75
76
|
diff_score_version_notes: Optional[list] = None,
|
|
76
77
|
version: Version = LATEST_VERSION,
|
|
@@ -107,7 +108,7 @@ def matchfile_from_alignment(
|
|
|
107
108
|
Whether to assume that the part has been unfolded according to the
|
|
108
109
|
repetitions in the alignment. If False, the part will be automatically
|
|
109
110
|
unfolded to have maximal coverage of the notes in the alignment.
|
|
110
|
-
See `partitura.score.unfold_part_alignment
|
|
111
|
+
See `partitura.score.unfold_part_alignment`, defaults to True.
|
|
111
112
|
tempo_indication : str or None
|
|
112
113
|
The tempo direction indicated in the beginning of the score
|
|
113
114
|
diff_score_version_notes : list or None
|
|
@@ -210,18 +211,28 @@ def matchfile_from_alignment(
|
|
|
210
211
|
# Info for sorting lines
|
|
211
212
|
snote_sort_info = dict()
|
|
212
213
|
for (mnum, msd, msb), m in zip(measure_starts, measures):
|
|
214
|
+
if mnum == 0:
|
|
215
|
+
# handle offsets in anacrusis measure
|
|
216
|
+
ts_num, ts_den, _ = spart.time_signature_map(0)
|
|
217
|
+
dpq = int(spart.quarter_duration_map(0))
|
|
218
|
+
measure_dur_in_divs = m.end.t - m.start.t
|
|
219
|
+
expected_measure_dur = ts_num * 4 / ts_den * dpq
|
|
220
|
+
if measure_dur_in_divs < expected_measure_dur:
|
|
221
|
+
msd -= expected_measure_dur - measure_dur_in_divs
|
|
222
|
+
msb -= (expected_measure_dur - measure_dur_in_divs) / dpq * ts_den / 4
|
|
223
|
+
|
|
213
224
|
time_signatures = spart.iter_all(score.TimeSignature, m.start, m.end)
|
|
214
225
|
|
|
215
226
|
for tsig in time_signatures:
|
|
216
227
|
time_divs = int(tsig.start.t)
|
|
217
228
|
time_beats = float(beat_map(time_divs))
|
|
229
|
+
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
218
230
|
dpq = int(spart.quarter_duration_map(time_divs))
|
|
231
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
219
232
|
beat = int((time_beats - msb) // 1)
|
|
220
233
|
|
|
221
|
-
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
222
|
-
|
|
223
234
|
moffset_divs = Fraction(
|
|
224
|
-
int(time_divs - msd - beat *
|
|
235
|
+
int(time_divs - msd - beat * divs_per_beat), int(ts_den * divs_per_beat)
|
|
225
236
|
)
|
|
226
237
|
|
|
227
238
|
scoreprop_lines["time_signatures"].append(
|
|
@@ -247,15 +258,15 @@ def matchfile_from_alignment(
|
|
|
247
258
|
key_signatures = spart.iter_all(score.KeySignature, m.start, m.end)
|
|
248
259
|
|
|
249
260
|
for ksig in key_signatures:
|
|
250
|
-
time_divs = int(
|
|
261
|
+
time_divs = int(ksig.start.t)
|
|
251
262
|
time_beats = float(beat_map(time_divs))
|
|
263
|
+
ts_num, ts_den, _ = spart.time_signature_map(ksig.start.t)
|
|
252
264
|
dpq = int(spart.quarter_duration_map(time_divs))
|
|
265
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
253
266
|
beat = int((time_beats - msb) // 1)
|
|
254
267
|
|
|
255
|
-
ts_num, ts_den, _ = spart.time_signature_map(tsig.start.t)
|
|
256
|
-
|
|
257
268
|
moffset_divs = Fraction(
|
|
258
|
-
int(time_divs - msd - beat *
|
|
269
|
+
int(time_divs - msd - beat * divs_per_beat), int(ts_den * divs_per_beat)
|
|
259
270
|
)
|
|
260
271
|
|
|
261
272
|
scoreprop_lines["key_signatures"].append(
|
|
@@ -282,27 +293,28 @@ def matchfile_from_alignment(
|
|
|
282
293
|
snotes = spart.iter_all(score.Note, m.start, m.end, include_subclasses=True)
|
|
283
294
|
# Beginning of each measure
|
|
284
295
|
for snote in snotes:
|
|
285
|
-
onset_divs
|
|
296
|
+
onset_divs = snote.start.t
|
|
297
|
+
offset_divs = snote.start.t + snote.duration_tied
|
|
286
298
|
duration_divs = offset_divs - onset_divs
|
|
287
|
-
|
|
299
|
+
# beat computations
|
|
288
300
|
onset_beats, offset_beats = beat_map([onset_divs, offset_divs])
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
beat = int((onset_beats - msb) // 1)
|
|
293
|
-
|
|
301
|
+
duration_beats = offset_beats - onset_beats
|
|
302
|
+
beat = int((onset_beats - msb) // 1) # beat field of the snote
|
|
303
|
+
# quarter, div, symbolic computation
|
|
294
304
|
ts_num, ts_den, _ = spart.time_signature_map(snote.start.t)
|
|
295
|
-
|
|
296
|
-
duration_symb = Fraction(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
moffset_divs = Fraction(
|
|
305
|
+
dpq = int(spart.quarter_duration_map(onset_divs))
|
|
306
|
+
duration_symb = Fraction(
|
|
307
|
+
duration_divs, dpq * 4
|
|
308
|
+
) # compute duration from quarters/divs
|
|
309
|
+
divs_per_beat = 4 / ts_den * dpq
|
|
310
|
+
moffset_divs = Fraction(
|
|
311
|
+
int(onset_divs - msd - beat * divs_per_beat),
|
|
312
|
+
int(ts_den * divs_per_beat),
|
|
313
|
+
)
|
|
301
314
|
|
|
302
315
|
if debug:
|
|
303
|
-
duration_beats = offset_beats - onset_beats
|
|
304
316
|
moffset_beat = (onset_beats - msb - beat) / ts_den
|
|
305
|
-
assert np.isclose(float(duration_symb), duration_beats)
|
|
317
|
+
assert np.isclose(float(duration_symb), duration_beats / ts_den)
|
|
306
318
|
assert np.isclose(moffset_beat, float(moffset_divs))
|
|
307
319
|
|
|
308
320
|
score_attributes_list = []
|
|
@@ -508,9 +520,7 @@ def matchfile_from_alignment(
|
|
|
508
520
|
|
|
509
521
|
# Concatenate all lines
|
|
510
522
|
all_match_lines += list(note_lines) + pedal_lines
|
|
511
|
-
|
|
512
523
|
matchfile = MatchFile(lines=all_match_lines)
|
|
513
|
-
|
|
514
524
|
return matchfile
|
|
515
525
|
|
|
516
526
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"""
|
|
4
4
|
This module contains methods for exporting MIDI files
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
import numpy as np
|
|
7
8
|
|
|
8
9
|
from collections import defaultdict, OrderedDict
|
|
@@ -36,34 +37,34 @@ def map_to_track_channel(note_keys, mode):
|
|
|
36
37
|
if mode == 0:
|
|
37
38
|
trk = tr_helper.setdefault(p, len(tr_helper))
|
|
38
39
|
ch1 = ch_helper.setdefault(p, {})
|
|
39
|
-
ch2 = ch1.setdefault(v, len(ch1)
|
|
40
|
+
ch2 = ch1.setdefault(v, len(ch1))
|
|
40
41
|
track[(pg, p, v)] = trk
|
|
41
42
|
channel[(pg, p, v)] = ch2
|
|
42
43
|
elif mode == 1:
|
|
43
44
|
trk = tr_helper.setdefault(pg, len(tr_helper))
|
|
44
45
|
ch1 = ch_helper.setdefault(pg, {})
|
|
45
|
-
ch2 = ch1.setdefault(p, len(ch1)
|
|
46
|
+
ch2 = ch1.setdefault(p, len(ch1))
|
|
46
47
|
track[(pg, p, v)] = trk
|
|
47
48
|
channel[(pg, p, v)] = ch2
|
|
48
49
|
elif mode == 2:
|
|
49
50
|
track[(pg, p, v)] = 0
|
|
50
|
-
ch = ch_helper.setdefault(p, len(ch_helper)
|
|
51
|
+
ch = ch_helper.setdefault(p, len(ch_helper))
|
|
51
52
|
channel[(pg, p, v)] = ch
|
|
52
53
|
elif mode == 3:
|
|
53
54
|
trk = tr_helper.setdefault(p, len(tr_helper))
|
|
54
55
|
track[(pg, p, v)] = trk
|
|
55
|
-
channel[(pg, p, v)] =
|
|
56
|
+
channel[(pg, p, v)] = 0
|
|
56
57
|
elif mode == 4:
|
|
57
58
|
track[(pg, p, v)] = 0
|
|
58
|
-
channel[(pg, p, v)] =
|
|
59
|
+
channel[(pg, p, v)] = 0
|
|
59
60
|
elif mode == 5:
|
|
60
61
|
trk = tr_helper.setdefault((p, v), len(tr_helper))
|
|
61
62
|
track[(pg, p, v)] = trk
|
|
62
|
-
channel[(pg, p, v)] =
|
|
63
|
+
channel[(pg, p, v)] = 0
|
|
63
64
|
else:
|
|
64
65
|
raise Exception("unsupported part/voice assign mode {}".format(mode))
|
|
65
66
|
|
|
66
|
-
result = dict((k, (track.get(k, 0), channel.get(k,
|
|
67
|
+
result = dict((k, (track.get(k, 0), channel.get(k, 0))) for k in note_keys)
|
|
67
68
|
# for (pg, p, voice), v in result.items():
|
|
68
69
|
# pgn = pg.group_name if hasattr(pg, 'group_name') else pg.id
|
|
69
70
|
# print(pgn, p.id, voice)
|
|
@@ -177,7 +178,7 @@ def save_performance_midi(
|
|
|
177
178
|
|
|
178
179
|
for c in performed_part.controls:
|
|
179
180
|
track = c.get("track", 0)
|
|
180
|
-
ch = c.get("channel",
|
|
181
|
+
ch = c.get("channel", 0)
|
|
181
182
|
t = int(np.round(10**6 * ppq * c["time"] / mpq))
|
|
182
183
|
track_events[track][t].append(
|
|
183
184
|
Message(
|
|
@@ -190,7 +191,7 @@ def save_performance_midi(
|
|
|
190
191
|
|
|
191
192
|
for n in performed_part.notes:
|
|
192
193
|
track = n.get("track", 0)
|
|
193
|
-
ch = n.get("channel",
|
|
194
|
+
ch = n.get("channel", 0)
|
|
194
195
|
t_on = int(np.round(10**6 * ppq * n["note_on"] / mpq))
|
|
195
196
|
t_off = int(np.round(10**6 * ppq * n["note_off"] / mpq))
|
|
196
197
|
vel = n.get("velocity", default_velocity)
|
|
@@ -203,7 +204,7 @@ def save_performance_midi(
|
|
|
203
204
|
|
|
204
205
|
for p in performed_part.programs:
|
|
205
206
|
track = p.get("track", 0)
|
|
206
|
-
ch = p.get("channel",
|
|
207
|
+
ch = p.get("channel", 0)
|
|
207
208
|
t = int(np.round(10**6 * ppq * p["time"] / mpq))
|
|
208
209
|
track_events[track][t].append(
|
|
209
210
|
Message("program_change", program=int(p["program"]), channel=ch)
|
|
@@ -215,11 +216,11 @@ def save_performance_midi(
|
|
|
215
216
|
list(
|
|
216
217
|
set(
|
|
217
218
|
[
|
|
218
|
-
(c.get("channel",
|
|
219
|
+
(c.get("channel", 0), c.get("track", 0))
|
|
219
220
|
for c in performed_part.controls
|
|
220
221
|
]
|
|
221
222
|
+ [
|
|
222
|
-
(n.get("channel",
|
|
223
|
+
(n.get("channel", 0), n.get("track", 0))
|
|
223
224
|
for n in performed_part.notes
|
|
224
225
|
]
|
|
225
226
|
)
|
|
@@ -395,7 +396,7 @@ def save_score_midi(
|
|
|
395
396
|
|
|
396
397
|
def to_ppq(t):
|
|
397
398
|
# convert div times to new ppq
|
|
398
|
-
return
|
|
399
|
+
return round(ppq * (qm(t) - ftp))
|
|
399
400
|
|
|
400
401
|
for tp in part.iter_all(score.Tempo):
|
|
401
402
|
tempos[to_ppq(tp.start.t)] = MetaMessage(
|
|
@@ -405,6 +406,12 @@ def save_score_midi(
|
|
|
405
406
|
if not tempos:
|
|
406
407
|
tempos[0] = MetaMessage("set_tempo", tempo=500000)
|
|
407
408
|
|
|
409
|
+
# note velocities
|
|
410
|
+
velocities = []
|
|
411
|
+
for vp in part.iter_all(score.Dynamic):
|
|
412
|
+
velocities.append((vp.start.t, round(90 * vp.velocity / 100)))
|
|
413
|
+
velocities = np.stack(velocities) if velocities else velocities
|
|
414
|
+
|
|
408
415
|
if anacrusis_behavior == "time_sig_change":
|
|
409
416
|
# Change time signature to match the duration of the measure
|
|
410
417
|
# This ensure the beat and downbeats position are coherent
|
|
@@ -486,11 +493,17 @@ def save_score_midi(
|
|
|
486
493
|
# key is a tuple (part_group, part, voice) that will be
|
|
487
494
|
# converted into a (track, channel) pair.
|
|
488
495
|
key = (pg, part, note.voice)
|
|
496
|
+
vel = velocity
|
|
497
|
+
if len(velocities) > 0:
|
|
498
|
+
vel_idx = (
|
|
499
|
+
np.searchsorted(velocities[:, 0], note.start.t, side="right") - 1
|
|
500
|
+
)
|
|
501
|
+
vel = int(velocities[vel_idx, 1])
|
|
489
502
|
events[key][to_ppq(note.start.t)].append(
|
|
490
|
-
Message("note_on", note=note.midi_pitch)
|
|
503
|
+
Message("note_on", note=note.midi_pitch, velocity=vel)
|
|
491
504
|
)
|
|
492
505
|
events[key][to_ppq(note.start.t + note.duration_tied)].append(
|
|
493
|
-
Message("note_off", note=note.midi_pitch)
|
|
506
|
+
Message("note_off", note=note.midi_pitch, velocity=vel)
|
|
494
507
|
)
|
|
495
508
|
event_keys[key] = True
|
|
496
509
|
|