partitura 1.4.1__tar.gz → 1.6.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 (111) hide show
  1. {partitura-1.4.1 → partitura-1.6.0}/PKG-INFO +13 -2
  2. {partitura-1.4.1 → partitura-1.6.0}/partitura/__init__.py +6 -1
  3. partitura-1.6.0/partitura/assets/score_example.mei +69 -0
  4. {partitura-1.4.1 → partitura-1.6.0}/partitura/directions.py +1 -23
  5. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/__init__.py +39 -9
  6. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/exportaudio.py +79 -4
  7. partitura-1.6.0/partitura/io/exportkern.py +344 -0
  8. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/exportmatch.py +8 -3
  9. partitura-1.6.0/partitura/io/exportmei.py +645 -0
  10. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/exportmidi.py +41 -1
  11. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/exportmusicxml.py +66 -10
  12. partitura-1.6.0/partitura/io/importdcml.py +353 -0
  13. partitura-1.6.0/partitura/io/importkern.py +913 -0
  14. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importmatch.py +90 -88
  15. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importmei.py +48 -12
  16. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importmidi.py +183 -29
  17. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importmusic21.py +15 -9
  18. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importmusicxml.py +225 -12
  19. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importnakamura.py +1 -1
  20. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/matchfile_utils.py +2 -1
  21. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/matchlines_v1.py +8 -6
  22. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/key_identification.py +10 -67
  23. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/meter.py +14 -16
  24. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/note_array_to_score.py +1 -1
  25. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/note_features.py +138 -8
  26. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/performance_codec.py +36 -24
  27. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/performance_features.py +1 -1
  28. {partitura-1.4.1 → partitura-1.6.0}/partitura/performance.py +37 -7
  29. {partitura-1.4.1 → partitura-1.6.0}/partitura/score.py +1507 -308
  30. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/__init__.py +4 -0
  31. partitura-1.6.0/partitura/utils/fluidsynth.py +326 -0
  32. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/generic.py +34 -2
  33. partitura-1.6.0/partitura/utils/globals.py +743 -0
  34. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/misc.py +48 -1
  35. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/music.py +251 -313
  36. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/normalize.py +1 -3
  37. {partitura-1.4.1 → partitura-1.6.0}/partitura/utils/synth.py +18 -38
  38. {partitura-1.4.1 → partitura-1.6.0}/partitura.egg-info/PKG-INFO +13 -2
  39. {partitura-1.4.1 → partitura-1.6.0}/partitura.egg-info/SOURCES.txt +9 -1
  40. {partitura-1.4.1 → partitura-1.6.0}/setup.py +1 -1
  41. partitura-1.6.0/tests/test_clef.py +105 -0
  42. partitura-1.6.0/tests/test_cross_staff.py +34 -0
  43. partitura-1.6.0/tests/test_dcml_import.py +20 -0
  44. partitura-1.6.0/tests/test_fluidsynth.py +97 -0
  45. partitura-1.6.0/tests/test_kern.py +84 -0
  46. {partitura-1.4.1 → partitura-1.6.0}/tests/test_mei.py +45 -10
  47. {partitura-1.4.1 → partitura-1.6.0}/tests/test_merge_parts.py +10 -1
  48. {partitura-1.4.1 → partitura-1.6.0}/tests/test_metrical_position.py +2 -0
  49. {partitura-1.4.1 → partitura-1.6.0}/tests/test_midi_import.py +60 -2
  50. {partitura-1.4.1 → partitura-1.6.0}/tests/test_note_array.py +3 -5
  51. {partitura-1.4.1 → partitura-1.6.0}/tests/test_note_features.py +17 -1
  52. partitura-1.6.0/tests/test_performance.py +332 -0
  53. partitura-1.6.0/tests/test_performance_features.py +46 -0
  54. {partitura-1.4.1 → partitura-1.6.0}/tests/test_synth.py +1 -1
  55. partitura-1.6.0/tests/test_urlload.py +26 -0
  56. {partitura-1.4.1 → partitura-1.6.0}/tests/test_utils.py +111 -45
  57. {partitura-1.4.1 → partitura-1.6.0}/tests/test_xml.py +64 -0
  58. partitura-1.4.1/partitura/assets/score_example.mei +0 -56
  59. partitura-1.4.1/partitura/io/exportmei.py +0 -2096
  60. partitura-1.4.1/partitura/io/importkern.py +0 -626
  61. partitura-1.4.1/tests/test_cross_staff_beaming.py +0 -20
  62. partitura-1.4.1/tests/test_kern.py +0 -48
  63. partitura-1.4.1/tests/test_performance.py +0 -140
  64. partitura-1.4.1/tests/test_performance_features.py +0 -42
  65. {partitura-1.4.1 → partitura-1.6.0}/LICENSE +0 -0
  66. {partitura-1.4.1 → partitura-1.6.0}/README.md +0 -0
  67. {partitura-1.4.1 → partitura-1.6.0}/partitura/assets/musicxml.xsd +0 -0
  68. {partitura-1.4.1 → partitura-1.6.0}/partitura/assets/score_example.krn +0 -0
  69. {partitura-1.4.1 → partitura-1.6.0}/partitura/assets/score_example.mid +0 -0
  70. {partitura-1.4.1 → partitura-1.6.0}/partitura/assets/score_example.musicxml +0 -0
  71. {partitura-1.4.1 → partitura-1.6.0}/partitura/display.py +0 -0
  72. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/exportparangonada.py +0 -0
  73. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/importparangonada.py +0 -0
  74. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/matchfile_base.py +0 -0
  75. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/matchlines_v0.py +0 -0
  76. {partitura-1.4.1 → partitura-1.6.0}/partitura/io/musescore.py +0 -0
  77. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/__init__.py +0 -0
  78. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/pitch_spelling.py +0 -0
  79. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/tonal_tension.py +0 -0
  80. {partitura-1.4.1 → partitura-1.6.0}/partitura/musicanalysis/voice_separation.py +0 -0
  81. {partitura-1.4.1 → partitura-1.6.0}/partitura.egg-info/dependency_links.txt +0 -0
  82. {partitura-1.4.1 → partitura-1.6.0}/partitura.egg-info/requires.txt +0 -0
  83. {partitura-1.4.1 → partitura-1.6.0}/partitura.egg-info/top_level.txt +0 -0
  84. {partitura-1.4.1 → partitura-1.6.0}/setup.cfg +0 -0
  85. {partitura-1.4.1 → partitura-1.6.0}/tests/test_deprecations.py +0 -0
  86. {partitura-1.4.1 → partitura-1.6.0}/tests/test_display.py +0 -0
  87. {partitura-1.4.1 → partitura-1.6.0}/tests/test_harmony.py +0 -0
  88. {partitura-1.4.1 → partitura-1.6.0}/tests/test_key_estimation.py +0 -0
  89. {partitura-1.4.1 → partitura-1.6.0}/tests/test_load_performance.py +0 -0
  90. {partitura-1.4.1 → partitura-1.6.0}/tests/test_load_score.py +0 -0
  91. {partitura-1.4.1 → partitura-1.6.0}/tests/test_m21_import.py +0 -0
  92. {partitura-1.4.1 → partitura-1.6.0}/tests/test_match_export.py +0 -0
  93. {partitura-1.4.1 → partitura-1.6.0}/tests/test_match_import.py +0 -0
  94. {partitura-1.4.1 → partitura-1.6.0}/tests/test_midi_export.py +0 -0
  95. {partitura-1.4.1 → partitura-1.6.0}/tests/test_musescore.py +0 -0
  96. {partitura-1.4.1 → partitura-1.6.0}/tests/test_nakamura.py +0 -0
  97. {partitura-1.4.1 → partitura-1.6.0}/tests/test_new_divs.py +0 -0
  98. {partitura-1.4.1 → partitura-1.6.0}/tests/test_octave_shift.py +0 -0
  99. {partitura-1.4.1 → partitura-1.6.0}/tests/test_parangonada.py +0 -0
  100. {partitura-1.4.1 → partitura-1.6.0}/tests/test_part_properties.py +0 -0
  101. {partitura-1.4.1 → partitura-1.6.0}/tests/test_partial_measures.py +0 -0
  102. {partitura-1.4.1 → partitura-1.6.0}/tests/test_performance_codec.py +0 -0
  103. {partitura-1.4.1 → partitura-1.6.0}/tests/test_pianoroll.py +0 -0
  104. {partitura-1.4.1 → partitura-1.6.0}/tests/test_pitch_spelling.py +0 -0
  105. {partitura-1.4.1 → partitura-1.6.0}/tests/test_quarter_adjust.py +0 -0
  106. {partitura-1.4.1 → partitura-1.6.0}/tests/test_rest_array.py +0 -0
  107. {partitura-1.4.1 → partitura-1.6.0}/tests/test_time_estimation.py +0 -0
  108. {partitura-1.4.1 → partitura-1.6.0}/tests/test_times.py +0 -0
  109. {partitura-1.4.1 → partitura-1.6.0}/tests/test_tonal_tension.py +0 -0
  110. {partitura-1.4.1 → partitura-1.6.0}/tests/test_transpose.py +0 -0
  111. {partitura-1.4.1 → partitura-1.6.0}/tests/test_voice_estimation.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: partitura
3
- Version: 1.4.1
3
+ Version: 1.6.0
4
4
  Summary: A package for handling symbolic musical information
5
5
  Home-page: https://github.com/CPJKU/partitura
6
6
  Author: Maarten Grachten, Carlos Cancino-Chacón, Silvan Peter, Emmanouil Karystinaios, Francesco Foscarin, Thassilo Gadermaier, Patricia Hu
@@ -21,6 +21,17 @@ Requires-Dist: lxml
21
21
  Requires-Dist: lark-parser
22
22
  Requires-Dist: xmlschema
23
23
  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
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
24
35
 
25
36
 
26
37
  [//]: # (<p align="center"> )
@@ -15,6 +15,7 @@ from .io.exportmusicxml import save_musicxml
15
15
  from .io.importmei import load_mei
16
16
  from .io.importkern import load_kern
17
17
  from .io.importmusic21 import load_music21
18
+ from .io.importdcml import load_dcml
18
19
  from .io.importmidi import load_score_midi, load_performance_midi, midi_to_notearray
19
20
  from .io.exportmidi import save_score_midi, save_performance_midi
20
21
  from .io.importmatch import load_match
@@ -22,7 +23,8 @@ from .io.exportmatch import save_match
22
23
  from .io.importnakamura import load_nakamuramatch, load_nakamuracorresp
23
24
  from .io.importparangonada import load_parangonada_csv
24
25
  from .io.exportparangonada import save_parangonada_csv, save_csv_for_parangonada
25
- from .io.exportaudio import save_wav
26
+ from .io.exportaudio import save_wav, save_wav_fluidsynth
27
+ from .io.exportmei import save_mei
26
28
  from .display import render
27
29
  from . import musicanalysis
28
30
  from .musicanalysis import make_note_features, compute_note_array, full_note_array
@@ -56,9 +58,12 @@ __all__ = [
56
58
  "save_performance_midi",
57
59
  "load_match",
58
60
  "save_match",
61
+ "load_dcml",
59
62
  "load_nakamuramatch",
60
63
  "load_nakamuracorresp",
61
64
  "load_parangonada_csv",
62
65
  "save_parangonada_csv",
66
+ "save_wav",
67
+ "save_wav_fluidsynth",
63
68
  "render",
64
69
  ]
@@ -0,0 +1,69 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <?xml-model href="https://music-encoding.org/schema/5.0/mei-all.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>
3
+ <?xml-model href="https://music-encoding.org/schema/5.0/mei-all.rng" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>
4
+ <mei xmlns="http://www.music-encoding.org/ns/mei" meiversion="5.0">
5
+ <meiHead xml:id="m21agkk">
6
+ <fileDesc xml:id="f1x9ud6p">
7
+ <titleStmt xml:id="t1pe4slb">
8
+ <title />
9
+ <respStmt />
10
+ </titleStmt>
11
+ <pubStmt xml:id="p1kxcrok">
12
+ <date isodate="2024-10-30" type="encoding-date">2024-10-30</date>
13
+ </pubStmt>
14
+ </fileDesc>
15
+ <encodingDesc xml:id="e1xr2zpp">
16
+ <appInfo xml:id="a1wyxc5n">
17
+ <application xml:id="a1t43ge2" isodate="2024-10-30T10:56:32" version="4.3.1-3b8cc17">
18
+ <name xml:id="n1cp60l4">Verovio</name>
19
+ <p xml:id="p13nndma">Transcoded from MusicXML</p>
20
+ </application>
21
+ </appInfo>
22
+ </encodingDesc>
23
+ </meiHead>
24
+ <music>
25
+ <body>
26
+ <mdiv xml:id="muo97v6">
27
+ <score xml:id="suneqlv">
28
+ <scoreDef xml:id="sz00r05">
29
+ <staffGrp xml:id="s1h35kps">
30
+ <staffGrp xml:id="P1" bar.thru="true">
31
+ <grpSym xml:id="g1eji31e" symbol="brace" />
32
+ <label xml:id="l8lirjj">Piano</label>
33
+ <instrDef xml:id="iggd40h" midi.channel="0" midi.instrnum="0" midi.volume="78.00%" />
34
+ <staffDef xml:id="sbpks8p" n="1" lines="5" ppq="1">
35
+ <clef xml:id="c1p7nrnw" shape="G" line="2" />
36
+ <keySig xml:id="ki5anqv" sig="0" />
37
+ <meterSig xml:id="m1vpw2w2" count="4" unit="4" />
38
+ </staffDef>
39
+ <staffDef xml:id="s1g416nz" n="2" lines="5" ppq="1">
40
+ <clef xml:id="cezkswz" shape="G" line="2" />
41
+ <keySig xml:id="kk41xfz" sig="0" />
42
+ <meterSig xml:id="m7alo7s" count="4" unit="4" />
43
+ </staffDef>
44
+ </staffGrp>
45
+ </staffGrp>
46
+ </scoreDef>
47
+ <section xml:id="suu4o7p">
48
+ <measure xml:id="m1e358qx" n="1">
49
+ <staff xml:id="s1ufvigy" n="1">
50
+ <layer xml:id="l1r7cvga" n="1">
51
+ <rest xml:id="ro1o7cb" dur.ppq="2" dur="2" />
52
+ <chord xml:id="c1c1r6b7" dur.ppq="2" dur="2" stem.dir="down">
53
+ <note xml:id="n6dpu2p" oct="5" pname="c" />
54
+ <note xml:id="njfgcwp" oct="5" pname="e" />
55
+ </chord>
56
+ </layer>
57
+ </staff>
58
+ <staff xml:id="s710zw2" n="2">
59
+ <layer xml:id="leffrs2" n="5">
60
+ <note xml:id="n1txt37q" dur.ppq="4" dur="1" oct="4" pname="a" />
61
+ </layer>
62
+ </staff>
63
+ </measure>
64
+ </section>
65
+ </score>
66
+ </mdiv>
67
+ </body>
68
+ </music>
69
+ </mei>
@@ -13,6 +13,7 @@ The functionality is provided by the function `parse_words`
13
13
 
14
14
  import re
15
15
  import warnings
16
+ from partitura.utils.globals import UNABBREVS
16
17
 
17
18
  try:
18
19
  from lark import Lark
@@ -51,29 +52,6 @@ def join_items(items):
51
52
  )
52
53
 
53
54
 
54
- UNABBREVS = [
55
- (re.compile(r"(crescendo|cresc\.?)"), "crescendo"),
56
- (re.compile(r"(smorzando|smorz\.?)"), "smorzando"),
57
- (re.compile(r"(decrescendo|(decresc|decr|dimin|dim)\.?)"), "diminuendo"),
58
- (re.compile(r"((acceler|accel|acc)\.?)"), "accelerando"),
59
- (re.compile(r"(ritenente|riten\.?)"), "ritenuto"),
60
- (re.compile(r"((ritard|rit)\.?)"), "ritardando"),
61
- (re.compile(r"((rallent|rall)\.?)"), "rallentando"),
62
- (re.compile(r"(dolciss\.?)"), "dolcissimo"),
63
- (re.compile(r"((sosten|sost)\.?)"), "sostenuto"),
64
- (re.compile(r"(delicatiss\.?)"), "delicatissimo"),
65
- (re.compile(r"(leggieramente|leggiermente|leggiero|legg\.?)"), "leggiero"),
66
- (re.compile(r"(leggierissimo|(leggieriss\.?))"), "leggierissimo"),
67
- (re.compile(r"(scherz\.?)"), "scherzando"),
68
- (re.compile(r"(tenute|ten\.?)"), "tenuto"),
69
- (re.compile(r"(allegretto)"), "allegro"),
70
- (re.compile(r"(espress\.?)"), "espressivo"),
71
- (re.compile(r"(ligato)"), "legato"),
72
- (re.compile(r"(ligatissimo)"), "legatissimo"),
73
- (re.compile(r"((rinforz|rinf|rfz|rf)\.?)"), "rinforzando"),
74
- ]
75
-
76
-
77
55
  def unabbreviate(s):
78
56
  for p, v in UNABBREVS:
79
57
  if p.match(s):
@@ -5,17 +5,21 @@ This module contains methods for importing and exporting symbolic music formats.
5
5
  """
6
6
  from typing import Union
7
7
  import os
8
-
8
+ import urllib.request
9
+ from urllib.parse import urlparse
10
+ import tempfile
9
11
  from .importmusicxml import load_musicxml
10
12
  from .importmidi import load_score_midi, load_performance_midi
11
13
  from .musescore import load_via_musescore
12
14
  from .importmatch import load_match
13
15
  from .importmei import load_mei
14
16
  from .importkern import load_kern
17
+ from .exportkern import save_kern
15
18
  from .importparangonada import load_parangonada_csv
16
19
  from .exportparangonada import save_parangonada_csv
17
20
  from .importmusic21 import load_music21
18
-
21
+ from .exportmei import save_mei
22
+ from .importdcml import load_dcml
19
23
  from partitura.utils.misc import (
20
24
  deprecated_alias,
21
25
  deprecated_parameter,
@@ -30,6 +34,14 @@ class NotSupportedFormatError(Exception):
30
34
  pass
31
35
 
32
36
 
37
+ def is_url(input):
38
+ try:
39
+ result = urlparse(input)
40
+ return all([result.scheme, result.netloc])
41
+ except ValueError:
42
+ return False
43
+
44
+
33
45
  @deprecated_alias(score_fn="filename")
34
46
  @deprecated_parameter("ensure_list")
35
47
  def load_score(filename: PathLike, force_note_ids="keep") -> Score:
@@ -55,11 +67,29 @@ def load_score(filename: PathLike, force_note_ids="keep") -> Score:
55
67
  scr: :class:`partitura.score.Score`
56
68
  A score instance.
57
69
  """
70
+ if is_url(filename):
71
+ url = filename
72
+ # Send a GET request to the URL
73
+ with urllib.request.urlopen(url) as response:
74
+ data = response.read()
75
+
76
+ # Extract the file extension from the URL
77
+ extension = os.path.splitext(url)[-1]
78
+
79
+ # Create a temporary file
80
+ temp_file = tempfile.NamedTemporaryFile(suffix=extension, delete=True)
81
+
82
+ # Write the content to the temporary file
83
+ with open(temp_file.name, "wb") as f:
84
+ f.write(data)
85
+
86
+ filename = temp_file.name
87
+ else:
88
+ extension = os.path.splitext(filename)[-1].lower()
58
89
 
59
- extension = os.path.splitext(filename)[-1].lower()
60
90
  if extension in (".mxl", ".xml", ".musicxml"):
61
91
  # Load MusicXML
62
- return load_musicxml(
92
+ score = load_musicxml(
63
93
  filename=filename,
64
94
  force_note_ids=force_note_ids,
65
95
  )
@@ -69,15 +99,15 @@ def load_score(filename: PathLike, force_note_ids="keep") -> Score:
69
99
  assign_note_ids = False
70
100
  else:
71
101
  assign_note_ids = True
72
- return load_score_midi(
102
+ score = load_score_midi(
73
103
  filename=filename,
74
104
  assign_note_ids=assign_note_ids,
75
105
  )
76
106
  elif extension in [".mei"]:
77
107
  # Load MEI
78
- return load_mei(filename=filename)
108
+ score = load_mei(filename=filename)
79
109
  elif extension in [".kern", ".krn"]:
80
- return load_kern(
110
+ score = load_kern(
81
111
  filename=filename,
82
112
  force_note_ids=force_note_ids,
83
113
  )
@@ -105,7 +135,7 @@ def load_score(filename: PathLike, force_note_ids="keep") -> Score:
105
135
  ".gp",
106
136
  ]:
107
137
  # Load MuseScore
108
- return load_via_musescore(
138
+ score = load_via_musescore(
109
139
  filename=filename,
110
140
  force_note_ids=force_note_ids,
111
141
  )
@@ -115,11 +145,11 @@ def load_score(filename: PathLike, force_note_ids="keep") -> Score:
115
145
  filename=filename,
116
146
  create_score=True,
117
147
  )
118
- return score
119
148
  else:
120
149
  raise NotSupportedFormatError(
121
150
  f"{extension} file extension is not supported. If this should be supported, consider editing partitura/io/__init__.py file"
122
151
  )
152
+ return score
123
153
 
124
154
 
125
155
  def load_score_as_part(filename: PathLike) -> Part:
@@ -1,8 +1,7 @@
1
1
  #!/usr/bin/python
2
2
  # -*- coding: utf-8 -*-
3
3
  """
4
- This module contains methods to synthesize Partitura object to wav using
5
- additive synthesis
4
+ This module contains methods to synthesize a Partitura ScoreLike object to wav.
6
5
  """
7
6
  from typing import Union, Optional, Callable, Dict, Any
8
7
  import numpy as np
@@ -13,10 +12,18 @@ from partitura.score import ScoreLike
13
12
  from partitura.performance import PerformanceLike
14
13
 
15
14
  from partitura.utils.synth import synthesize, SAMPLE_RATE, A4
15
+ from partitura.utils.fluidsynth import (
16
+ synthesize_fluidsynth,
17
+ DEFAULT_SOUNDFONT,
18
+ HAS_FLUIDSYNTH,
19
+ )
16
20
 
17
21
  from partitura.utils.misc import PathLike
18
22
 
19
- __all__ = ["save_wav"]
23
+ __all__ = [
24
+ "save_wav",
25
+ "save_wav_fluidsynth",
26
+ ]
20
27
 
21
28
 
22
29
  def save_wav(
@@ -89,8 +96,76 @@ def save_wav(
89
96
  bpm=bpm,
90
97
  )
91
98
 
99
+ if out is not None:
100
+ # convert to 16bit integers (save as PCM 16 bit)
101
+ # (some DAWs cannot load audio files that are float64,
102
+ # e.g., Logic)
103
+ amplitude = np.iinfo(np.int16).max
104
+ if abs(audio_signal).max() <= 1:
105
+ # convert to 16bit integers (save as PCM 16 bit)
106
+ amplitude = np.iinfo(np.int16).max
107
+ audio_signal *= amplitude
108
+ wavfile.write(out, samplerate, audio_signal.astype(np.int16))
109
+
110
+ else:
111
+ return audio_signal
112
+
113
+
114
+ def save_wav_fluidsynth(
115
+ input_data: Union[ScoreLike, PerformanceLike, np.ndarray],
116
+ out: Optional[PathLike] = None,
117
+ samplerate: int = SAMPLE_RATE,
118
+ soundfont: PathLike = DEFAULT_SOUNDFONT,
119
+ bpm: Union[float, np.ndarray, Callable] = 60,
120
+ ) -> Optional[np.ndarray]:
121
+ """
122
+ Export a score (a `Score`, `Part`, `PartGroup` or list of `Part` instances),
123
+ a performance (`Performance`, `PerformedPart` or list of `PerformedPart` instances)
124
+ as a WAV file using fluidsynth
125
+
126
+ Parameters
127
+ ----------
128
+ input_data : ScoreLike, PerformanceLike or np.ndarray
129
+ A partitura object with note information.
130
+ out : PathLike or None
131
+ Path of the output Wave file. If None, the method outputs
132
+ the audio signal as an array (see `audio_signal` below).
133
+ samplerate: int
134
+ The sample rate of the audio file in Hz. The default is 44100Hz.
135
+ soundfont : PathLike
136
+ Path to the soundfont in SF2/SF3 format for fluidsynth.
137
+ bpm : float, np.ndarray, callable
138
+ The bpm to render the output (if the input is a score-like object).
139
+ See `partitura.utils.music.performance_notearray_from_score_notearray`
140
+ for more information on this parameter.
141
+
142
+ Returns
143
+ -------
144
+ audio_signal : np.ndarray
145
+ Audio signal as a 1D array. Only returned if `out` is None.
146
+ """
147
+
148
+ if not HAS_FLUIDSYNTH:
149
+ raise ImportError("Fluidsynth is not installed!")
150
+
151
+ audio_signal = synthesize_fluidsynth(
152
+ note_info=input_data,
153
+ samplerate=samplerate,
154
+ soundfont=soundfont,
155
+ bpm=bpm,
156
+ )
157
+
92
158
  if out is not None:
93
159
  # Write audio signal
94
- wavfile.write(out, samplerate, audio_signal)
160
+
161
+ # convert to 16bit integers (save as PCM 16 bit)
162
+ # (some DAWs cannot load audio files that are float64,
163
+ # e.g., Logic)
164
+ amplitude = np.iinfo(np.int16).max
165
+ if abs(audio_signal).max() <= 1:
166
+ # convert to 16bit integers (save as PCM 16 bit)
167
+ amplitude = np.iinfo(np.int16).max
168
+ audio_signal *= amplitude
169
+ wavfile.write(out, samplerate, audio_signal.astype(np.int16))
95
170
  else:
96
171
  return audio_signal