partitura 1.7.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.
Files changed (110) hide show
  1. {partitura-1.7.0 → partitura-1.8.0}/PKG-INFO +32 -18
  2. partitura-1.8.0/docs/source/conf.py +120 -0
  3. {partitura-1.7.0 → partitura-1.8.0}/partitura/__init__.py +8 -9
  4. {partitura-1.7.0 → partitura-1.8.0}/partitura/directions.py +12 -2
  5. {partitura-1.7.0 → partitura-1.8.0}/partitura/display.py +4 -4
  6. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/__init__.py +1 -0
  7. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportaudio.py +1 -0
  8. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportkern.py +6 -1
  9. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportmatch.py +1 -0
  10. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportmidi.py +15 -2
  11. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportmusicxml.py +25 -0
  12. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importdcml.py +8 -3
  13. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importmatch.py +4 -1
  14. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importmei.py +1 -0
  15. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importmidi.py +1 -0
  16. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importmusicxml.py +54 -20
  17. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importparangonada.py +1 -0
  18. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/matchfile_base.py +1 -0
  19. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/matchfile_utils.py +1 -0
  20. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/matchlines_v0.py +1 -0
  21. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/matchlines_v1.py +1 -0
  22. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/__init__.py +0 -1
  23. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/key_identification.py +1 -0
  24. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/note_features.py +1 -0
  25. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/performance_codec.py +32 -16
  26. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/performance_features.py +1 -2
  27. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/pitch_spelling.py +1 -0
  28. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/voice_separation.py +1 -0
  29. {partitura-1.7.0 → partitura-1.8.0}/partitura/performance.py +0 -1
  30. {partitura-1.7.0 → partitura-1.8.0}/partitura/score.py +44 -8
  31. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/generic.py +5 -17
  32. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/globals.py +0 -1
  33. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/misc.py +1 -0
  34. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/normalize.py +1 -0
  35. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/synth.py +1 -0
  36. {partitura-1.7.0 → partitura-1.8.0}/partitura.egg-info/PKG-INFO +32 -18
  37. {partitura-1.7.0 → partitura-1.8.0}/partitura.egg-info/SOURCES.txt +2 -2
  38. partitura-1.8.0/partitura.egg-info/requires.txt +31 -0
  39. partitura-1.8.0/partitura.egg-info/top_level.txt +4 -0
  40. partitura-1.8.0/pyproject.toml +92 -0
  41. partitura-1.8.0/tests/test_harmony.py +387 -0
  42. {partitura-1.7.0 → partitura-1.8.0}/tests/test_midi_export.py +10 -0
  43. {partitura-1.7.0 → partitura-1.8.0}/tests/test_note_features.py +14 -0
  44. {partitura-1.7.0 → partitura-1.8.0}/tests/test_pianoroll.py +2 -1
  45. {partitura-1.7.0 → partitura-1.8.0}/tests/test_synth.py +2 -2
  46. {partitura-1.7.0 → partitura-1.8.0}/tests/test_times.py +4 -7
  47. partitura-1.7.0/partitura.egg-info/requires.txt +0 -6
  48. partitura-1.7.0/partitura.egg-info/top_level.txt +0 -1
  49. partitura-1.7.0/setup.py +0 -79
  50. partitura-1.7.0/tests/test_harmony.py +0 -31
  51. partitura-1.7.0/tests/test_quarter_adjust.py +0 -157
  52. {partitura-1.7.0 → partitura-1.8.0}/LICENSE +0 -0
  53. {partitura-1.7.0 → partitura-1.8.0}/README.md +0 -0
  54. {partitura-1.7.0 → partitura-1.8.0}/partitura/assets/musicxml.xsd +0 -0
  55. {partitura-1.7.0 → partitura-1.8.0}/partitura/assets/score_example.krn +0 -0
  56. {partitura-1.7.0 → partitura-1.8.0}/partitura/assets/score_example.mei +0 -0
  57. {partitura-1.7.0 → partitura-1.8.0}/partitura/assets/score_example.mid +0 -0
  58. {partitura-1.7.0 → partitura-1.8.0}/partitura/assets/score_example.musicxml +0 -0
  59. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportmei.py +1 -1
  60. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/exportparangonada.py +0 -0
  61. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importkern.py +1 -1
  62. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importmusic21.py +0 -0
  63. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/importnakamura.py +1 -1
  64. {partitura-1.7.0 → partitura-1.8.0}/partitura/io/musescore.py +0 -0
  65. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/meter.py +0 -0
  66. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/note_array_to_score.py +0 -0
  67. {partitura-1.7.0 → partitura-1.8.0}/partitura/musicanalysis/tonal_tension.py +1 -1
  68. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/__init__.py +0 -0
  69. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/fluidsynth.py +0 -0
  70. {partitura-1.7.0 → partitura-1.8.0}/partitura/utils/music.py +1 -1
  71. {partitura-1.7.0 → partitura-1.8.0}/partitura.egg-info/dependency_links.txt +0 -0
  72. {partitura-1.7.0 → partitura-1.8.0}/setup.cfg +0 -0
  73. {partitura-1.7.0 → partitura-1.8.0}/tests/test_clef.py +0 -0
  74. {partitura-1.7.0 → partitura-1.8.0}/tests/test_cross_staff.py +0 -0
  75. {partitura-1.7.0 → partitura-1.8.0}/tests/test_dcml_import.py +0 -0
  76. {partitura-1.7.0 → partitura-1.8.0}/tests/test_deprecations.py +0 -0
  77. {partitura-1.7.0 → partitura-1.8.0}/tests/test_display.py +0 -0
  78. {partitura-1.7.0 → partitura-1.8.0}/tests/test_fluidsynth.py +0 -0
  79. {partitura-1.7.0 → partitura-1.8.0}/tests/test_iter.py +0 -0
  80. {partitura-1.7.0 → partitura-1.8.0}/tests/test_kern.py +0 -0
  81. {partitura-1.7.0 → partitura-1.8.0}/tests/test_key_estimation.py +0 -0
  82. {partitura-1.7.0 → partitura-1.8.0}/tests/test_load_performance.py +0 -0
  83. {partitura-1.7.0 → partitura-1.8.0}/tests/test_load_score.py +0 -0
  84. {partitura-1.7.0 → partitura-1.8.0}/tests/test_m21_import.py +0 -0
  85. {partitura-1.7.0 → partitura-1.8.0}/tests/test_match_export.py +0 -0
  86. {partitura-1.7.0 → partitura-1.8.0}/tests/test_match_import.py +0 -0
  87. {partitura-1.7.0 → partitura-1.8.0}/tests/test_mei.py +0 -0
  88. {partitura-1.7.0 → partitura-1.8.0}/tests/test_merge_parts.py +0 -0
  89. {partitura-1.7.0 → partitura-1.8.0}/tests/test_metrical_position.py +0 -0
  90. {partitura-1.7.0 → partitura-1.8.0}/tests/test_midi_import.py +0 -0
  91. {partitura-1.7.0 → partitura-1.8.0}/tests/test_musescore.py +0 -0
  92. {partitura-1.7.0 → partitura-1.8.0}/tests/test_nakamura.py +0 -0
  93. {partitura-1.7.0 → partitura-1.8.0}/tests/test_new_divs.py +0 -0
  94. {partitura-1.7.0 → partitura-1.8.0}/tests/test_note_array.py +0 -0
  95. {partitura-1.7.0 → partitura-1.8.0}/tests/test_octave_shift.py +0 -0
  96. {partitura-1.7.0 → partitura-1.8.0}/tests/test_parangonada.py +0 -0
  97. {partitura-1.7.0 → partitura-1.8.0}/tests/test_part_properties.py +0 -0
  98. {partitura-1.7.0 → partitura-1.8.0}/tests/test_partial_measures.py +0 -0
  99. {partitura-1.7.0 → partitura-1.8.0}/tests/test_performance.py +0 -0
  100. {partitura-1.7.0 → partitura-1.8.0}/tests/test_performance_codec.py +0 -0
  101. {partitura-1.7.0 → partitura-1.8.0}/tests/test_performance_features.py +0 -0
  102. {partitura-1.7.0 → partitura-1.8.0}/tests/test_pitch_spelling.py +0 -0
  103. {partitura-1.7.0 → partitura-1.8.0}/tests/test_rest_array.py +0 -0
  104. {partitura-1.7.0 → partitura-1.8.0}/tests/test_time_estimation.py +0 -0
  105. {partitura-1.7.0 → partitura-1.8.0}/tests/test_tonal_tension.py +0 -0
  106. {partitura-1.7.0 → partitura-1.8.0}/tests/test_transpose.py +0 -0
  107. {partitura-1.7.0 → partitura-1.8.0}/tests/test_urlload.py +0 -0
  108. {partitura-1.7.0 → partitura-1.8.0}/tests/test_utils.py +0 -0
  109. {partitura-1.7.0 → partitura-1.8.0}/tests/test_voice_estimation.py +0 -0
  110. {partitura-1.7.0 → partitura-1.8.0}/tests/test_xml.py +0 -0
@@ -1,18 +1,21 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: partitura
3
- Version: 1.7.0
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
- Author-email: partitura-users@googlegroups.com
6
+ Maintainer-email: partitura-users@googlegroups.com
8
7
  License: Apache 2.0
9
- Keywords: music notation musicxml midi
10
- Classifier: License :: OSI Approved :: MIT License
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.7
18
+ Requires-Python: >=3.10
16
19
  Description-Content-Type: text/markdown
17
20
  License-File: LICENSE
18
21
  Requires-Dist: numpy
@@ -21,19 +24,30 @@ Requires-Dist: lxml
21
24
  Requires-Dist: lark-parser
22
25
  Requires-Dist: xmlschema
23
26
  Requires-Dist: mido
24
- Dynamic: author
25
- Dynamic: author-email
26
- Dynamic: classifier
27
- Dynamic: description
28
- Dynamic: description-content-type
29
- Dynamic: home-page
30
- Dynamic: keywords
31
- Dynamic: license
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"
32
50
  Dynamic: license-file
33
- Dynamic: requires-dist
34
- Dynamic: requires-python
35
- Dynamic: summary
36
-
37
51
 
38
52
  [//]: # (<p align="center"> )
39
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 pkg_resources
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__ = pkg_resources.get_distribution("partitura").version
35
+ __version__ = version("partitura")
35
36
 
36
37
  #: An example MusicXML file for didactic purposes
37
- EXAMPLE_MUSICXML = pkg_resources.resource_filename(
38
- "partitura", "assets/score_example.musicxml"
39
- )
38
+ EXAMPLE_MUSICXML = str(files("partitura") / "assets" / "score_example.musicxml")
40
39
 
41
- EXAMPLE_MIDI = pkg_resources.resource_filename("partitura", "assets/score_example.mid")
42
- EXAMPLE_MEI = pkg_resources.resource_filename("partitura", "assets/score_example.mei")
43
- EXAMPLE_KERN = pkg_resources.resource_filename("partitura", "assets/score_example.krn")
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
- start = tree.column - 1
343
+ col = check_tree(tree, "column")
344
+ if col is not None:
345
+ start = col - 1
338
346
  if end is None:
339
- end = tree.end_column - 1
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 TemporaryFile() as xml_fh, NamedTemporaryFile(
121
- suffix=prvw_sfx, delete=False
122
- ) as img_fh:
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,6 +3,7 @@
3
3
  """
4
4
  This module contains methods for importing and exporting symbolic music formats.
5
5
  """
6
+
6
7
  from typing import Union
7
8
  import os
8
9
  import urllib.request
@@ -3,6 +3,7 @@
3
3
  """
4
4
  This module contains methods to synthesize a Partitura ScoreLike object to wav.
5
5
  """
6
+
6
7
  from typing import Union, Optional, Callable, Dict, Any
7
8
  import numpy as np
8
9
  from scipy.io import wavfile
@@ -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 1.6.0"
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
@@ -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
@@ -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
 
@@ -3,6 +3,7 @@
3
3
  """
4
4
  This module contains methods for exporting MusicXML files.
5
5
  """
6
+
6
7
  import math
7
8
  from collections import defaultdict
8
9
  from lxml import etree
@@ -777,6 +778,7 @@ def do_directions(part, start, end, counter):
777
778
  result.append(elem)
778
779
 
779
780
  tempos = part.iter_all(score.Tempo, start, end)
781
+ dynamics = part.iter_all(score.Dynamic, start, end)
780
782
  directions = part.iter_all(score.Direction, start, end, include_subclasses=True)
781
783
 
782
784
  for tempo in tempos:
@@ -791,6 +793,10 @@ def do_directions(part, start, end, counter):
791
793
  )
792
794
  result.append((tempo.start.t, None, e3))
793
795
 
796
+ for dynamic in dynamics:
797
+ e3 = etree.Element("sound", dynamics="{}".format(dynamic.velocity))
798
+ result.append((dynamic.start.t, None, e3))
799
+
794
800
  for direction in directions:
795
801
  text = direction.raw_text or direction.text
796
802
 
@@ -907,6 +913,9 @@ def do_harmony(part, start, end):
907
913
  root_e = etree.SubElement(harmony_e, "root")
908
914
  root_step_e = etree.SubElement(root_e, "root-step")
909
915
  root_step_e.text = h.root
916
+ if h.alter != 0:
917
+ root_alter_e = etree.SubElement(root_e, "root-alter")
918
+ root_alter_e.text = str(h.alter)
910
919
  if h.bass is not None:
911
920
  bass_e = etree.SubElement(harmony_e, "bass")
912
921
  bass_step_e = etree.SubElement(bass_e, "bass-step")
@@ -1048,6 +1057,22 @@ def save_musicxml(
1048
1057
 
1049
1058
  root = etree.Element("score-partwise")
1050
1059
 
1060
+ # add work metadata
1061
+ if score_data.work_title or score_data.work_number:
1062
+ work_tag = etree.SubElement(root, "work")
1063
+ if score_data.work_title:
1064
+ work_title_tag = etree.SubElement(work_tag, "work_title")
1065
+ work_title_tag.text = str(score_data.work_title)
1066
+ if score_data.work_number:
1067
+ work_number_tag = etree.SubElement(work_tag, "work_number")
1068
+ work_number_tag.text = str(score_data.work_number)
1069
+ if score_data.movement_title:
1070
+ movement_title_tag = etree.SubElement(root, "movement_title")
1071
+ movement_title_tag.text = str(score_data.movement_title)
1072
+ if score_data.movement_number:
1073
+ movement_number_tag = etree.SubElement(root, "movement_number")
1074
+ movement_number_tag.text = str(score_data.movement_number)
1075
+
1051
1076
  partlist_e = etree.SubElement(root, "part-list")
1052
1077
  state = {
1053
1078
  "note_id_counter": {},
@@ -4,6 +4,8 @@ from math import ceil
4
4
  import partitura.score as spt
5
5
  from partitura.score import process_local_key
6
6
  from partitura.utils.music import estimate_symbolic_duration
7
+ from pandas.api.types import is_object_dtype
8
+ from pandas.api.types import is_string_dtype
7
9
 
8
10
  try:
9
11
  import pandas as pd
@@ -19,7 +21,8 @@ def read_note_tsv(note_tsv_path, metadata=None):
19
21
  data = data[~data["quarterbeats"].isna()]
20
22
  data["quarterbeats"] = (
21
23
  data["quarterbeats"].apply(eval)
22
- if data.dtypes["quarterbeats"] == str or data.dtypes["quarterbeats"] == object
24
+ if is_string_dtype(data.dtypes["quarterbeats"])
25
+ or is_object_dtype(data.dtypes["quarterbeats"])
23
26
  else data["quarterbeats"]
24
27
  )
25
28
  unique_durations = data["duration"].unique()
@@ -204,7 +207,8 @@ def read_measure_tsv(measure_tsv_path, part):
204
207
  data = data[~data["quarterbeats"].isna()]
205
208
  data["quarterbeats"] = (
206
209
  data["quarterbeats"].apply(eval)
207
- if data.dtypes["quarterbeats"] == str or data.dtypes["quarterbeats"] == object
210
+ if is_string_dtype(data.dtypes["quarterbeats"])
211
+ or is_object_dtype(data.dtypes["quarterbeats"])
208
212
  else data["quarterbeats"]
209
213
  )
210
214
  data["onset_div"] = np.array([int(qd * qdivs) for qd in data["quarterbeats"]])
@@ -239,7 +243,8 @@ def read_harmony_tsv(beat_tsv_path, part):
239
243
  data = data[~data["quarterbeats"].isna()]
240
244
  data["quarterbeats"] = (
241
245
  data["quarterbeats"].apply(eval)
242
- if data.dtypes["quarterbeats"] == str or data.dtypes["quarterbeats"] == object
246
+ if is_string_dtype(data.dtypes["quarterbeats"])
247
+ or is_object_dtype(data.dtypes["quarterbeats"])
243
248
  else data["quarterbeats"]
244
249
  )
245
250
  data["onset_div"] = np.array([int(qd * qdivs) for qd in data["quarterbeats"]])
@@ -3,6 +3,7 @@
3
3
  """
4
4
  This module contains methods for parsing matchfiles
5
5
  """
6
+
6
7
  import os
7
8
  from typing import Union, Tuple, Optional, Callable, List
8
9
  import warnings
@@ -516,7 +517,9 @@ def part_from_matchfile(
516
517
 
517
518
  # ___ these divs are relative to quarters;
518
519
  divs = np.lcm.reduce(np.unique(divs_arg))
519
- onset_in_divs = np.r_[0, np.cumsum(divs * iois_in_quarters, dtype=int)][inv_idxs]
520
+ onset_in_divs = np.r_[0, np.cumsum(np.round(divs * iois_in_quarters), dtype=int)][
521
+ inv_idxs
522
+ ]
520
523
  onset_in_quarters = onset_in_quarters[inv_idxs]
521
524
 
522
525
  part.set_quarter_duration(0, divs)
@@ -3,6 +3,7 @@
3
3
  """
4
4
  This module contains methods for importing MEI files.
5
5
  """
6
+
6
7
  import os
7
8
  from collections import OrderedDict
8
9
  from lxml import etree
@@ -3,6 +3,7 @@
3
3
  """
4
4
  This module contains methods for importing MIDI files.
5
5
  """
6
+
6
7
  import warnings
7
8
 
8
9
  from collections import defaultdict