Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utilities to split scores into chunks #380

Open
leleogere opened this issue Oct 1, 2024 · 8 comments
Open

Utilities to split scores into chunks #380

leleogere opened this issue Oct 1, 2024 · 8 comments

Comments

@leleogere
Copy link

leleogere commented Oct 1, 2024

Hello,

First of all, thank you for your work!

I was wondering if Partitura has utilities to split scores (and aligned performances) into chunks of n measures. For what I've seen, it seems not (it should be possible by hand, but I wanted to ask first just in case I missed something).

If that's not the case, I think that it could be a nice enhancement. One purpose of Partitura is to apply machine/deep learning techniques to symbolic music, and it could help to build datasets for such applications. Sometimes, scores are too large to fit in one part in some training contexts, and it would be nice to be able to feed models with chunks of n measures extracted from the score. To be useful, I think such splits should take the context into account: if I extract measures 4 to 8, it should gather earlier some time signature/key into the newly created chunk.

Ideally, it would be very interesting to be able to link this with an aligned performance, so that I could easily take the measures 4-8 of the score, as well as the measures 4-8 of the performance.

@sildater
Copy link
Member

sildater commented Oct 2, 2024

There is no built-in functionality to do this, but some workarounds are possible:

  1. extract note arrays from performance and score possibly with extra information using note features and performance features
  2. merge the score and performance note arrays with an existing alignment like so :
# matched idx in sna and pna
matched_idx = pt.musicanalysis.performance_codec.get_matched_notes(sna, pna, alignment)
# get subsets
pna_aligned_subset = pna[matched_idx[:,1]]
sna_aligned_subset = sna[matched_idx[:,0]]
# merge arrays (make sure no field names collide)
merged_aligned_note_array = rfn.merge_arrays([pna_aligned_subset, sna_aligned_subset], flatten=True, usemask=False)
  1. create a mask for the merged note array related to measures you'd like to extract using Measure note features, the part.measure_map or some custom code using the general iterator part.iter_all:
for measure in part.iter_all(partitura.score.Measure):
# your processing here

I hope that helps!

@leleogere
Copy link
Author

leleogere commented Oct 2, 2024

Thanks for your help! However, this would not allow exporting back each score part as musicxml, does it? Extracting the note array results in loosing some information (rests, context information), right?

Ideally, I would like to be able to input a score and a performance, and being able to split the score and the performances into chunks of n measures, like score_m1-m4/perf_m1-m4, score_m5-m9/perf_m5-m9... (Or even with a sliding window like score_m1-m4/perf_m1-m4, score_m2-m5/perf_m2-m5...).

I'm currently working on something along those lines. I'll post it here once finished.

EDIT: I need exporting them back into musicxml as some baselines I want to work with deal directly with musicxml without using partitura.

@sildater
Copy link
Member

sildater commented Oct 2, 2024

ah I see in this case I'd check out the score variant

scorevariant = partitura.score.ScoreVariant(part)
scorevariant.add_segment(
    starting_measure.start,ending_measure.end
)
new_part = scorevariant.create_variant_part()

be aware that the partitura musicxml export might not support all attributes you're interested in, the output scores might look different.

@leleogere
Copy link
Author

Oh I missed this object, I'm going to take a look at it!

@leleogere
Copy link
Author

This seems to kind of work, but not all the time. I tried the following little script:

from pathlib import Path

import partitura as pt
import partitura.score

SCORE_PATH = Path('/home/user/Documents/datasets/nasap-dataset/Rachmaninoff/Preludes_op_32/5/xml_score.musicxml')
MAX_MEASURES = 10
NUM_MEASURES_PER_CHUNK = 3
OUT_DIR = Path("/tmp/score_parts")

score = pt.load_musicxml(SCORE_PATH)
assert len(score.parts) == 1
part = score.parts[0]

OUT_DIR.mkdir(exist_ok=True)

for start_measure_idx in range(0, MAX_MEASURES):
    end_measure_idx = start_measure_idx + NUM_MEASURES_PER_CHUNK
    print(f"Chunk {start_measure_idx}-{end_measure_idx}")

    start_measure = part.measures[start_measure_idx]
    end_measure = part.measures[end_measure_idx - 1]

    print(start_measure)
    print(end_measure)

    # Create a new ScoreVariant
    sv = pt.score.ScoreVariant(part)
    sv.add_segment(start_measure.start, end_measure.end)
    new_chunk = sv.create_variant_part()

    # Set the new part
    pt.save_musicxml(new_chunk, OUT_DIR / f"chunk_{start_measure_idx}-{end_measure_idx}.musicxml")

with a Rachmaninoff piece from (n)-ASAP. The first chunks seem ok (except that apart for the very first one key/time signatures are missing, but adding them manually won't be a problem), but it seems that some issues appear at measure 9, with some weird rendering of the quintuplet (quintuplets do not cause issue in the first measures) and MuseScore complaining that the xml has issues.

Original measure 9:
Original measure 9

New measure 9 after splitting using ScoreVariant:
New measure 9 after split

I don't know what causes this behavior, but when I manually build the chunks by iterating on all elements and copying them into a new part, this measure is correctly exported.

@sildater
Copy link
Member

sildater commented Oct 3, 2024

good catch! Maybe we should open a separate issue for this ScoreVariant bug? Could you also attach your implementation iterating over all objects and creating a new part for comparison? Thanks a lot!

@leleogere
Copy link
Author

I'll do that tomorrow!

@leleogere
Copy link
Author

I opened the issue, to summarize, the problem seems to come from a bug in how ScoreVariant sets the quarter times of the newly created part.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants