11# ruff: noqa: E402
22
3+ import hashlib
34import json
45import multiprocessing
56import 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"""
149208In mscx file, tuplet can be set invisible via the following ways:
1502091. <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