Skip to content

Commit f4251b9

Browse files
committed
Different music fonts
1 parent 75a367e commit f4251b9

1 file changed

Lines changed: 64 additions & 2 deletions

File tree

training/omr_datasets/convert_lieder.py

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# ruff: noqa: E402
22

3+
import hashlib
34
import json
45
import multiprocessing
56
import os
@@ -119,7 +120,9 @@ def copy_all_mscx_files(working_dir: str, dest: str) -> None:
119120
shutil.copyfile(source, os.path.join(dest, file))
120121

121122

122-
def create_formats(source_file: str, formats: list[str]) -> list[dict[str, str]]:
123+
def create_formats(
124+
source_file: str, formats: list[str], style_file: str | None = None
125+
) -> list[dict[str, str]]:
123126
jobs: list[dict[str, str]] = []
124127

125128
# sq8940236: MuseScore seems to hang up
@@ -141,10 +144,66 @@ def create_formats(source_file: str, formats: list[str]) -> list[dict[str, str]]
141144
"in": source_file,
142145
"out": out_name,
143146
}
147+
if style_file is not None:
148+
job["style"] = style_file
144149
jobs.append(job)
145150
return jobs
146151

147152

153+
# Every Lieder page is rendered by MuseScore with its default "Leland" engraving font,
154+
# so the model only ever sees one glyph vocabulary for noteheads/clefs/accidentals/etc.
155+
# MuseScore ships several other SMuFL-compliant engraving fonts (selectable in the app
156+
# under Format > Style > Score > Musical Symbols, backed by a swappable <musicalSymbolFont>
157+
# style setting); rotating through them per piece costs nothing at render time and gives
158+
# the model exposure to multiple glyph "handwritings" without needing a different dataset
159+
# or renderer. This only varies glyph shapes, not MuseScore's own layout/spacing engine -
160+
# so it doesn't substitute for training on genuinely different renderers (Primus,
161+
# grandstaff), just cheaply widens the glyph diversity within Lieder itself.
162+
_MUSIC_FONTS = ["Leland", "Bravura", "Petaluma", "MuseJazz", "Gonville"]
163+
_music_font_style_dir = os.path.join(dataset_root, "MuseScoreStyles")
164+
165+
166+
def _music_font_style_file(font: str) -> str:
167+
return os.path.join(_music_font_style_dir, f"{font.replace(' ', '_')}.mss")
168+
169+
170+
def _ensure_music_font_style_files() -> None:
171+
"""
172+
Writes one minimal .mss style file per font in _MUSIC_FONTS (skipping ones that
173+
already exist), each just pointing MuseScore's musical-symbol and musical-text
174+
fonts at a single named font pair - MuseScore fills in every other style default.
175+
The "<Font> Text" naming for the paired text font mirrors the font-pair names
176+
MuseScore itself uses for its bundled fonts.
177+
"""
178+
os.makedirs(_music_font_style_dir, exist_ok=True)
179+
for font in _MUSIC_FONTS:
180+
path = _music_font_style_file(font)
181+
if os.path.exists(path):
182+
continue
183+
content = (
184+
'<?xml version="1.0" encoding="UTF-8"?>\n'
185+
'<museScore version="4.00">\n'
186+
" <Style>\n"
187+
f" <musicalSymbolFont>{font}</musicalSymbolFont>\n"
188+
f" <musicalTextFont>{font} Text</musicalTextFont>\n"
189+
" </Style>\n"
190+
"</museScore>\n"
191+
)
192+
with open(path, "w", encoding="utf-8") as f:
193+
f.write(content)
194+
195+
196+
def _music_font_for_file(source_file: str) -> str:
197+
"""
198+
Deterministic per-piece font choice (stable across reruns/partial recreates, so a
199+
piece doesn't silently re-render with a different font the next time this is run)
200+
based on a hash of the piece's own filename, not path or run order.
201+
"""
202+
stem = os.path.basename(source_file).split(".")[0]
203+
index = int(hashlib.sha256(stem.encode()).hexdigest(), 16) % len(_MUSIC_FONTS)
204+
return _MUSIC_FONTS[index]
205+
206+
148207
"""
149208
In mscx file, tuplet can be set invisible via the following ways:
150209
1. <Tuplet( id="...")>
@@ -272,13 +331,16 @@ def _create_musicxml_and_svg_files() -> None:
272331

273332
MuseScore = os.path.join(dataset_root, "MuseScore")
274333

334+
_ensure_music_font_style_files()
335+
275336
all_jobs = []
276337

277338
for file in mscx_files:
278339
_make_tuplet_visible(str(file))
279340
_make_staff_visible(str(file))
280341
_reset_note_positions(str(file))
281-
jobs = create_formats(str(file), ["musicxml", "svg"])
342+
style_file = _music_font_style_file(_music_font_for_file(str(file)))
343+
jobs = create_formats(str(file), ["musicxml", "svg"], style_file)
282344
all_jobs.extend(jobs)
283345

284346
if len(all_jobs) == 0:

0 commit comments

Comments
 (0)