-
Notifications
You must be signed in to change notification settings - Fork 401
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
MusicXML writer: Ottavas that start or end in the middle of a multi-voice measure get the wrong notes from the other voices #1718
Comments
Hi Greg -- can you make a minimum demo (like 2-4 notes) without importing a musicxml file (like just create the streams themselves) that has the problem? It gets very slow to step through the debugging to make this work. |
I'll do my best to make a simple example. |
Here is a very simple example that produces a MusicXML that leaves the notes from voice 2 out of the octave-shift:
And here is the resulting MusicXML file, which leaves f and g out of the octave-shift'ed section: This isn't a great example; let me work on one that adds notes to the octave-shift that shouldn't be there. |
Ah, here we go. Same music21 score, but this time do:
The ottava time span is exactly the same, but the resulting MusicXML file has c, d, e, f, and g (everything but a) in the octave-shift. Interestingly, if I replace that one line of code with:
(still the exact same ottava time span) we end up with a MusicXML file that has the octave-shift stop before the octave-shift start! |
One could argue that the output MusicXML is correct, and that MusicXML readers should (instead of taking all notes textually between octave-shift start and stop as being in the ottava) take note of the measure indices and offsets of the octave-shift start and stop, and then compute which notes in all the measures/voices are between those two timestamps, but Finale and Musescore don't do that. |
Hi Greg -- thanks for the great minimal example -- really fantastic. What's happening is Music21's AI is detecting that there's no way C4-D4 would be under an ottava-alta without F4-G4 also.... no, that's B.S. -- that'd be why a human would see it... that's not it. :-) The problem is:
the ott.fill() is working exactly as described:
Take this slightly different example: In [15]: ott3 = music21.spanner.Ottava()
In [16]: ott3.addSpannedElements((c, e))
In [17]: ott3.fill()
In [18]: ott3
Out[18]: <music21.spanner.Ottava
8va transposing
<music21.note.Note C>
<music21.note.Note D>
<music21.note.Note E>> here I'm not dismissing or saying that there isn't a problem with the MusicXML writer. I do believe though that in your example, Let's keep revising the example until we find something where music21 isn't behaving as intended -- I want to pin down whether the problem is on importing musicxml or writing musicxml. (or both). Here at least in "music21-space" everything is working how it should be. |
Yes that's right. The music21 data is exactly correct, but the MusicXML written from that data is wrong. music21 does a great job of figuring out what notes are affected by the ottava, and the MusicXML writer ignores that, and just puts an octave-shift start before the first note in the Ottava, and an octave-shift stop after the last note in the Ottava. This ends up being wrong because of the order of the measure's notes in the MusicXML file (all of voice 1, then all of voice 2). |
So no new examples needed, the bug is seen in those written files. |
Ah, now I think I see the problem; so it's again a backwards and forwards problem. There is a reason why a key design goal of MNX is not to have these elements. Given how complex the music21 musicxml voice-writing algorithm already is, I'm not sure that this is a bug that I'm ever going to have time to fix unless it unleashes a lot more benefit than just voices + ottavas ending mid-staff. (like if it affected slurs). I think in creating a score there is a work around that should work -- create one ottava for each voice and mark the second one as hideObjectOnPrint or something? but wow...not I get it and will think about it. What do other musicxml writers do? |
That's a really good question. I will play around a bit with Musescore and Finale (and Dorico) and see what they write. |
music21 version
9.2.0b2
Problem summary
MusicXML written from a music21 score that has an Ottava that ends in the middle of voice 2 will have the octave-shift stop after the appropriate note in voice 2, but the octave-shift'ed section will include all the notes in voice 1. I'm assuming (but haven't yet seen) that if the last note in the Ottava is in the middle of voice 1, then all the notes in voice 2 will be left out, even though some of them should be in the octave-shift'ed section. A similar thing can happen with an Ottava that starts in the middle of a multi-voice measure, with notes in the non-starting voice being left out or included inappropriately, depending on which voice comes first in the MusicXML file produced.
Steps to reproduce
The example I have is measures 127 and 128 of the right hand staff in Beethoven's Piano Sonata 32 Movement 2.
Measure 127's right hand staff has one voice, but measure 128's right hand staff has two voices. The Ottava spans from the first note in measure 127 through the first four notes in voice 2 of measure 128. Voice 1 in measure 128 has a hidden dotted quarter rest (which extends beyond the end of the ottava) followed by three 16th notes that are not in the Ottava. In the music21 score, all the correct notes are in the Ottava, and the start and end notes are correctly the very first and very last notes that should be octave-shifted (first note is the dotted eighth note E5, last note is the 16th note Ab4). The resulting written MusicXML file does have the octave-shift start just before the E5, and the octave-shift stop just after the Ab4 (yay), but... the entirety of measure 128's voice 1 is emitted before voice2, so all of voice 1 (including those three 16th notes) is incorrectly included in the octave-shift'ed section.
Measure127And128.musicxml.snippet.txt
Any advice?
I intend to fix this myself, but I need some advice about what the MusicXML should actually look like (I am no MusicXML expert).
The only solution I can come up with (still using the same example) is to emit (in measure 128) a partial voice 1 (up through the Ab4), then back up and emit partial voice 2 (just the hidden dotted quarter rest), then emit the octave-shift stop, then emit the rest of voice 1, then back up and emit the rest of voice 2. Of course this would have to be generalized to handle octave-shift start as well, and to deal with any number of voices, etc.
Do I have that right? I suspect I will have to do a quick scan for Ottavas that have this issue, because before I start exporting a measure to MusicXML I will need to know to do partial voices in that measure. I won't be able to wait until I encounter a note that is in an Ottava, since I may have already incorrectly emitted an entire voice at that point.
Any advice from @mscuthbert or @jacobtylerwalls?
The text was updated successfully, but these errors were encountered: