11# SyMuPe: Affective and Controllable Symbolic Music Performance
22
3- <img alt =" PianoFlow architecture " src =" assets/pianoflow.png " >
4-
53> Official release for the paper [ ** "SyMuPe: Affective and Controllable Symbolic Music Performance"** ] ( https://dl.acm.org/doi/10.1145/3746027.3755871 )
64> (** ACM MM 2025 Outstanding Paper Award** )
75>
1311> [ ![ ACM DL] ( https://img.shields.io/badge/MM_'25-Proceedings-19552e?logo=acm&logoColor=white )] ( https://dl.acm.org/doi/10.1145/3746027.3755871 )
1412> [ ![ Outstanding Paper Award] ( https://img.shields.io/badge/MM_'25-Outstanding_Paper-E6712D.svg )] ( https://acmmm2025.org/awards/ )
1513> [ ![ Website] ( https://img.shields.io/badge/Website-Demo-2563eb )] ( https://ilya16.github.io/SyMuPe )
16- > [ ![ Models] ( https://img.shields.io/badge/HuggingFace-Models-yellow?logo=huggingface )] ( https://huggingface.co/SyMuPe )
17- > [ ![ Dataset] ( https://img.shields.io/badge/HuggingFace-Dataset-yellow?logo=huggingface )] ( https://huggingface.co/datasets/SyMuPe/PERiScoPe )
14+ > [ ![ HF] ( https://img.shields.io/badge/HuggingFace-Models_&_Data-yellow?logo=huggingface )] ( https://huggingface.co/SyMuPe )
1815
1916## Description
2017
@@ -27,71 +24,194 @@ For more details, please refer to the [paper](https://arxiv.org/abs/2511.03425)
2724
2825## Install
2926
30- Install the ` symupe ` package using:
27+ Install ` symupe ` package using:
3128``` shell
32- pip install symupe
29+ pip install -U symupe
3330```
3431
35- ## Models in the SyMuPe Framework
32+ ## Models
33+
34+ Starting with v1.1.0, SyMuPe supports a * unified inference API* for the trained symbolic music models.
35+
36+ All models are grouped into three categories:
37+ 1 . ** Generators** (e.g. ` PerformanceGenerator ` )
38+ 2 . ** Classifiers** (e.g. ` MusicClassifier ` )
39+ 3 . ** Embedders** (e.g. ` MusicEmbedder ` )
40+
41+ The models can be loaded using the corresponding ` AutoFactory ` classes (` AutoGenerator ` , ` AutoClassifier ` , or ` AutoEmbedder ` ).
42+
43+ The trained models are available and documented on the [ Hugging Face Hub] ( https://huggingface.co/SyMuPe ) .
44+
45+ ### Score-to-Performance Rendering
3646
37- Score-only models described in the paper are available on the [ Hugging Face Hub] ( https://huggingface.co/SyMuPe ) .
47+ <img alt =" PianoFlow architecture " src =" https://raw.githubusercontent.com/ilya16/SyMuPe/main/assets/pianoflow.png " >
48+
49+ Score-only performance rendering models described in the paper are listed in the [ SyMuPe (ACM MM'25) Collection] ( https://huggingface.co/collections/SyMuPe/symupe-acm-mm25 ) on Hugging Face.
3850
3951| Model Repo | Type | Objective | Description |
4052| :---| :---------------------------| :---| :--------------------------------------------|
4153| [ ** PianoFlow-base** ] ( https://huggingface.co/SyMuPe/PianoFlow-base ) | Encoder Transformer | CFM | Flagship model for high-fidelity rendering |
4254| [ ** EncDec-base** ] ( https://huggingface.co/SyMuPe/EncDec-base ) | Encoder-Decoder Transformer | CLM | Slower sequence-to-sequence baseline |
4355| [ ** MLM-base** ] ( https://huggingface.co/SyMuPe/MLM-base ) | Encoder Transformer | MLM | Fast single-step language modeling baseline |
4456
45- ## Quick Start
57+ #### Quick Start
4658
47- Render an expressive performance from a quantized MIDI score in just a few lines of code.
59+ Render an expressive performance from a quantized MIDI score using an example code snippet:
4860
4961``` python
5062import torch
51- from symusic import Score
52-
53- from symupe.data.tokenizers import SyMuPe
54- from symupe.inference import AutoGenerator, perform_score, save_performances
55- from symupe.models import AutoModel
63+ from symupe import AutoGenerator
5664
5765device = torch.device(" cuda" if torch.cuda.is_available() else " cpu" )
5866
59- # Load the model and tokenizer directly from the Hub
67+ # Select model name from the Hub
6068model_name = " SyMuPe/PianoFlow-base"
6169# model_name = "SyMuPe/EncDec-base"
6270# model_name = "SyMuPe/MLM-base"
6371
64- model = AutoModel.from_pretrained(model_name).to(device)
65- tokenizer = SyMuPe.from_pretrained(model_name)
66-
67- # Prepare generator for the model
68- generator = AutoGenerator.from_model(model, tokenizer, device = device)
69-
70- # Load score MIDI
71- score_midi = Score(" score.mid" )
72+ # Build Generator by loading the model and tokenizer directly from the Hub
73+ generator = AutoGenerator.from_pretrained(model_name, device = device)
7274
7375# Perform score MIDI (tokenization is handled inside)
74- gen_results = perform_score(
75- generator = generator,
76- score = score_midi,
76+ gen_results = generator.perform_score(
77+ " score.mid" ,
7778 use_score_context = True ,
7879 num_samples = 8 ,
79- seed = 23
80+ seed = 23 ,
8081)
8182# gen_results[i] is PerformanceRenderingResult(...) containing:
8283# - score_midi, score_seq, gen_seq, perf_seq, perf_midi, perf_midi_sus
8384
8485# Save performed MIDI files in a single directory
85- save_performances(gen_results, out_dir = " samples" )
86+ generator.save_performances(gen_results, out_dir = " samples" )
87+ ```
8688
89+ ### MIDI Quality Classification
90+
91+ The [ MIDI Quality Classifier] ( https://huggingface.co/SyMuPe/MIDI-Quality-Classifier ) can be used to classify a MIDI file
92+ into one of the four quality classes: ` score ` , ` high quality ` , ` low quality ` , or ` corrupted ` .
93+
94+ The classifier was presented in the article [ ** "PianoCoRe: Combined and Refined Piano MIDI Dataset."** ] ( https://doi.org/10.5334/tismir.333 )
95+
96+ #### Quick Start
97+
98+ ``` python
99+ import torch
100+ from symupe import AutoClassifier
101+
102+ device = torch.device(" cuda" if torch.cuda.is_available() else " cpu" )
103+
104+ # Build Classifier by loading the model and tokenizer directly from the Hub
105+ classifier = AutoClassifier.from_pretrained(
106+ " SyMuPe/MIDI-Quality-Classifier" , device = device,
107+ )
108+ # model, tokenizer, labels = classifier.model, classifier.tokenizer, classifier.labels
109+
110+ # Classify MIDI (tokenization is handled inside)
111+ result = classifier(" performance.mid" )
112+ # result is MusicClassificationResult(...) containing:
113+ # - midi, seq, probabilities, prediction, label, all_logits, all_probabilities, all_predictions,
114+ # sequences and window_indices
115+ print (result.label, result.probabilities)
87116```
88117
89- ## Dataset
118+ ## Datasets and Data Processing
119+
120+ The SyMuPe project provides two MIDI datasets for analysing and modeling piano expression:
121+ 1 . The [ ** PERiScoPe** ] ( https://huggingface.co/datasets/SyMuPe/PERiScoPe ) ** (Piano Expression Refined Score and Performance MIDI)** dataset, used to train the models in the [ SyMuPe paper] ( https://arxiv.org/abs/2511.03425 ) .
122+ 2 . The [ ** PianoCoRe** ] ( https://github.com/ilya16/PianoCoRe ) ** (Combined and Refined Piano MIDI)** dataset, presented in the eponymous article published in the [ TISMIR journal] ( https://doi.org/10.5334/tismir.333 ) .
123+
124+ ### Refined Alignment for Scores and Performances
90125
91- The ** PERiScoPe** (Piano Expression Refined Score and Performance MIDI) dataset used to train the models is available on [ Hugging Face] ( https://huggingface.co/datasets/SyMuPe/PERiScoPe ) .
126+ The refined subset of PianoCoRe was constructed using the ** Refined Alignment for Scores and Performances (RAScoP)** pipeline,
127+ integrated into the SyMuPe package in v1.1.0. The algorithm is described in the [ article] ( https://doi.org/10.5334/tismir.333 ) .
128+
129+ #### Quick Start
130+
131+ Use the following quick start code to align the score and performance MIDI:
132+ ``` python
133+ from symupe.data.alignments import ParangonarAligner, RAScoPConfig, RAScoP
134+
135+ score_midi_path = " score.mid"
136+ perf_midi_path = " performance.mid"
137+
138+ # Align performance to score
139+ aligner = ParangonarAligner()
140+ alignment, paths, msgs = aligner.align(score_midi_path, perf_midi_path)
141+
142+ # Initialize RAScoP
143+ config = RAScoPConfig(
144+ score_holes = True ,
145+ performance_holes = True ,
146+ clean_onsets = True ,
147+ interpolate_notes = True ,
148+ synchronize_performance = False ,
149+ min_recall = 0.7 ,
150+ num_runs = 1 ,
151+ )
152+ rascop = RAScoP(
153+ score_midi = score_midi_path,
154+ perf_midi = perf_midi_path,
155+ alignment = alignment,
156+ config = config,
157+ verbose = 1 ,
158+ )
159+
160+ # Refine raw alignment
161+ alignment, match_ratios, stage_times = rascop()
162+ perf_midi, score_midi = rascop.perf_midi, rascop.score_midi # refined MIDI
163+ print (match_ratios)
164+ ```
165+
166+ Additional usage examples are available in the [ PianoCoRe repository] ( https://github.com/ilya16/PianoCoRe ) .
167+
168+ The RAScoP pipeline expects both the score and the performance MIDI files to be single-track.
169+ During initialization, all tracks are merged and duplicate notes are removed by default.
170+ For any other preprocessing, use ` symupe.data.midi.preprocess_midi ` :
171+ ``` python
172+ from symusic import Score
173+ from symupe.data.midi import preprocess_midi
174+
175+ midi = Score(" score.mid" )
176+ midi = preprocess_midi(
177+ midi,
178+ to_single_track = True ,
179+ clean_duplicates = True ,
180+ cut_overlapped_notes = True ,
181+ clean_short_notes = True ,
182+ min_tick_shift = 10 ,
183+ min_tick_duration = 5 ,
184+ target_ticks_per_quarter = 480 ,
185+ )
186+ midi.dump_midi(" score_processed.mid" )
187+ ```
188+
189+ ### MusicXML to MIDI conversion
190+
191+ SyMuPe provides utilities for working with MusicXML files. The code extends [ partitura] ( https://github.com/CPJKU/partitura )
192+ by supporting the processing of duplicate and overlapping notes, expansion of grace notes, and deduplication of ornament notes,
193+ as described in the [ PianoCoRe article] ( https://doi.org/10.5334/tismir.333 ) .
194+
195+ Use ` symupe.data.partitura.partitura_score_to_midi ` , to convert score files to MIDI similar to the PERiScoPe/PianoCoRe datasets:
196+ ``` python
197+ import partitura as pt
198+ from symupe.data.partitura import partitura_score_to_midi
199+
200+ score = pt.load_score(" score.musicxml" )
201+ midi = partitura_score_to_midi(
202+ score,
203+ expand_grace_notes = True ,
204+ process_ornaments = True ,
205+ clean_duplicates = True ,
206+ cut_overlapped_notes = True ,
207+ downsample_ticks_per_quarter = 48 ,
208+ ticks_per_quarter = 480 ,
209+ )
210+ ```
92211
93212## Citation
94213
214+ If you use the package, models or the PERiScoPe dataset in your research, please cite:
95215``` bibtex
96216@inproceedings{borovik2025symupe,
97217 title = {{SyMuPe: Affective and Controllable Symbolic Music Performance}},
@@ -103,8 +223,22 @@ The **PERiScoPe** (Piano Expression Refined Score and Performance MIDI) dataset
103223}
104224```
105225
226+ If you use the PianoCoRe dataset or the RAScoP pipeline for data processing, please cite:
227+ ``` bibtex
228+ @article{borovik2026pianocore,
229+ title = {{PianoCoRe: Combined and Refined Piano MIDI Dataset}},
230+ author = {Borovik, Ilya},
231+ year = {2026},
232+ journal = {Transactions of the International Society for Music Information Retrieval},
233+ volume = {9},
234+ number = {1},
235+ pages = {144--163},
236+ doi = {10.5334/tismir.333}
237+ }
238+ ```
239+
106240## License
107241
108242- The ** source code** in this repository is licensed under the [ Apache License 2.0] ( LICENSE ) .
109- - The ** pre- trained model weights** and the ** PERiScoPe dataset ** are licensed under
110- [ Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International ] ( LICENSE-DATA ) .
243+ - The ** trained model weights** and the ** datasets ** are licensed under
244+ [ CC BY-NC-SA 4.0] ( LICENSE-DATA ) license .
0 commit comments