Musescore 3.7 Evolution: pedal line

• Mar 13, 2024 - 11:32

Musescore 3.x is the only one that has support for sfz files either through zerbeus or through jack connections.
There are some piano sfz files out there that have separate files with samples with and without pedal (consequently with and without sympathetic resonance). Often such sfz files respond to CC 64, sustain pedal, to switch between with or without pedal.
I experimented some of these and noticed that whenever a pedal line was added, the first note or chord within the pedal line was played using the samples without pedal and only from the following notes on the pedal on samples were activated. I tried it in built-in zerbeus, through jack midi output and exporting midi and the results were always the same. Then I exported midi files and opened in midi editors. I found out that the cause is that wherever we draw a pedal line in musescore, the CC64 event is activated few milliseconds AFTER the note to which it was tied. That makes that note be played with CC64 off and therefore pedal off in sfz that depend on it.
Is it possible to correct such behavior in a way that when we draw a pedal line in the score the CC64 midi event occurs BEFORE the note where it was tied to?


Comments

in rendermidi.cpp I see:

      // create note & other events
      for (Staff* st : score->staves()) {
            StaffContext sctx;
            sctx.staff = st;
            sctx.method = renderMethod;
            sctx.cc = cc;
            sctx.renderHarmony = ctx.renderHarmony;
            renderStaffChunk(chunk, events, sctx);
            }
      events->fixupMIDI();
 
      // create sustain pedal events
      renderSpanners(chunk, events);

Do you mean it might help swapping the order here?

In reply to by Jojo-Schmitz

Unfortunately I wasn't able to figure out what everything in this code means. I imagine that swapping the order may help.
I can tell you how to reproduce what I described above so that you can try if changes in code work:
1 - Write a simple score and at some point draw a sustain pedal line.
2 - Export as midi.
3 - Use a midi editor that displays midi events list like synthfont, muse, the old midi locator etc.
4 - Observe that currently the CC64 event will appear some few midi ticks later than the exact beginning of that the first note where you placed the line.

So if your changes in code succeed it should appear at exactly the same midi tick where the first note to which the pedal is tied. I believe that if CC64 appears at the same midi tick as the note (regardless of being listed above or below it in the event list) it will produce the desired result. But the issue is that currently it appears few midi thicks later.

This situation can be seen in exported midi files, but it also reflects in Musescore real-time performance, either through zerbeus or through jack midi output.

In reply to by fernandoamartin

Hmm...

                  int st = s->tick().ticks();
                  if (st >= tick1 && st < tick2) {
                        // Handle "overlapping" pedal segments (usual case for connected pedal line)
                        if (lastEvent.second.first == false && lastEvent.first >= (st + tickOffset + 2)) {
                              channelPedalEvents.at(channel).pop_back();
                              channelPedalEvents.at(channel).push_back(std::pair<int, std::pair<bool, int> >(st + tickOffset + (2 - MScore::pedalEventsMinTicks), std::pair<bool, int>(false, staff)));
                              }
                        int a = st + tickOffset + 2;
                        channelPedalEvents.at(channel).push_back(std::pair<int, std::pair<bool, int> >(a, std::pair<bool, int>(true, staff)));
                        }
                  if (s->tick2().ticks() >= tick1 && s->tick2().ticks() <= tick2) {
                        int t = s->tick2().ticks() + tickOffset + (2 - MScore::pedalEventsMinTicks);
                        const RepeatSegment& lastRepeat = *score->repeatList().back();
                        if (t > lastRepeat.utick + lastRepeat.len())
                              t = lastRepeat.utick + lastRepeat.len();
                        channelPedalEvents.at(channel).push_back(std::pair<int, std::pair<bool, int> >(t, std::pair<bool, int>(false, staff)));
                        }

I'm suspicious about those "+ 2" there?

In reply to by worldwideweary

Now the CC64 appears at the same tick of the note. A nice progress. But unfortunately for automatized behavior with sfz files the simple fct that CC64 appears in the list after the note makes the switch in sound occur after the note. (I tested it in Musescore built-in Zerbeus synth.)
Is it possible to make the CC64 appear before the note in the event list?

Attachment Size
20240313_175733.jpg 79.09 KB

In reply to by Jojo-Schmitz

I tried using the file below and the sfz I sent you in github. Using Zerbeus in Musescore worked correctly at the beginning of the pedal line and the first note of measure 3 alerady started with program changed. However the pedal ended late. Instead of ending at the last tick of measure 3 it ended at the first tick of measure 4.

Attachment Size
Untitled.mscz 5.79 KB

In reply to by Jojo-Schmitz

I can't test breath controller because I don't have any software that uses it. MU inserts breath controllers at every measure unnecessarily. For me it looks like a code pollution that remained there with no real utility. But I don't know if there's a reason to be so.

In reply to by Jojo-Schmitz

Musescore is relatively old. I guess they originally designed it imagining that a pianist would hit the keys and press the pedal few milliseconds later. (Or maybe it was just a mistake.) But if a pianist wants to emphasize an isolate chord or isolated notes he must press the pedal few milliseconds before the hitting the keys in order that it vibrates with resonances. And this last possibility is what works better with today's software that have resonance samples.

I tested the build 8bb315f that was generated today and now we have a new bug. As seen at the image here CC64 is start 2 ticks after the note but it's ending probably 2 ticks later too. It was intended to stop at the end of measure 2. Instead the CC64 was turned off 1 tick after the first note of measure 3.
Can you revert the part of the code related to the pedal line to the way it was before we started this issue? (Builds from two days ago were not affected by this change.)
Having not found a suitable solution I suggest the workaround that I have been using until now and then we can live with it:
1 - Create an instrument xml with a customized piano that has two presets.
2 - Configure the sf2 or sfz file to have two separate presets, one with and other without resonance.
3 - When writing the score use staff text to switch between the two presets of item 2 here.
4 - Staff text can be copied and pasted or even added to a custom pallete to make their usage faster.
Result: Every pedal start and end we have to manually change the preset (a little more laborious but it works). We can't rely on CC64 to automatically change the used samples. In ther old code the end of pedalc line places CC64 at the right place, without invading the next measure. So if you bring back the old code we can workaround it in this longer way until we find a suitable solution someday.

Attachment Size
test.png 36.29 KB

It is indeed true that in most real world situations, pianists do press the pedal after the note. So moving it before would not normally be appropriate. What makes more sense is to have a property that could be set to alter this for some given pedal line, so that one desires these special resonance effects and is using a device that doesn't understand that resonance should apply to notes already sounding, you have a workaround.

But mostly this sounds like a bug in the device in question. It should recognize the pedal has been depressed and alter the sound of the already-playing notes accordingly. Granted, easier said than done.

In reply to by Marc Sabatella

I understand this possibility in real world. But Musescore is a software and it doesn't make sense in the realm of software for two reasons:
1. If pedal is activated after the note the sample without resonance was already played. You would need a high tech modeled resonance and software to activate a resonance after the note started to play. That doesn't work in some current formats, including sfz.
2. Turning off the pedal two ticks after the note ends makes all the notes that are already playing linger above the next chord. Imagine if you have a contemporary progression from C to C#. You draw pedal lime below notes in the C harmony and other pedal line below notes of the C# harmony. But currently CC64 would finish at the first note of C# harmony piling up a lot of dissonant notes. Most pianists use the pedal according to harmony changes, not after the changes to pile up dissonances.
(Understanding the two thicks issue allowed me to understand why some pieces I wrote, when played in modeled pianos like pianoteq and 4front truepianos, were piling up so many dissonant resonances while I was sure that I had finished the pedal line at the end of the previous chord.)

I think that moving a pedal start from little after the note/chord to little before the note/chord is not a good idea. Pianists do press pedal slightly after the note/chord. Also I'm sure that a high quality digital piano very well emulates the way the real piano sounds when it's played by a real pianist. And that is how it should be. So when MIDI data is recorded while pianist plays digital piano, the pedal start is recorded little after note/chord, not before. The same happens when this MIDI data is played back again on digital piano.
If there is a special need then it should be solved on a special way. Perhaps the most flexible way would be to have parameters in software: pedal start delay and pedal end delay, positive or negative numbers.

In reply to by worldwideweary

Yes, seems without my PR setting it to 3 should result in the Pedal to start 1 tick before the note (2 - 3 == -1), with the 2nd change of my PR, to write the pedal event before the note event, a 2 would be sufficient for the case at hand (2 - 2 == 0) here.
Default seems to to be 1, so pedal starts 1 tick afer the note

Why, oh why didn't I see this???

For Mu4 that would require that constant to be made available via an (advanced) prereferences, as that seems to have gotten lost in the transit

In reply to by Jojo-Schmitz

I tested in your latest build and no matter what I adjust in io/midi/pedaleventsminticks pedal always starts 2 ticks after the note. But worse than this is that it also ends 2 ticks after the note end, what makes it finish when the next chord is already on.
Maybe I didn't make the point clear above and what is being mostly discussed above is the pedal start. But image the mess it creates because the pedal ends 2 ticks after the not finished.

In reply to by fernandoamartin

Setting io/midi/pedaleventsminticks to 3.
Pedal starts at the same tick as the note and ends when the note is off.
That was the best result because pedal doesn't linger above the next chord anymore.
The strange thing is that io/midi/pedaleventsminticks has to be increased to reduce the ticks.
Here the pedal is still listed as a midi event after the note but at the same tick. However I don't think it's a big issue because if a pick software doesn't launch resonance due to it it won't be as noticeable as a pedal being still on over the next chord.

Attachment Size
tick3.jpg 82.55 KB

In reply to by fernandoamartin

> But image the mess it creates because the pedal ends 2 ticks after the note finished
Well, when a real pianists plays a real piano (or similarly when I play my cabinet digital piano), it is like that: a moment after releasing the previous chord and playing the next chord (pressing the keys), you release and then again press the pedal. So I think actually it's ok that pedal stop is 1 or 2 ticks after the previous chord was finished. Perhaps then, a pedal start should be 1 TICK AFTER a pedal stop, to ensure that the next pedal start happens after the previous pedal stop (when they are on the same chord). This is how real piano is played and how it sounds good. Generally speaking, digital instruments should emulate this as faithfully as they can and not only sound good if you play the pedal in some customized way.
Maybe you do not know but when you look at my digital piano (or any good quality digital piano) MIDI recording, you can see that it records the movement of the pedal smoothly (as a curve) and not simplified as a switch ON/OFF. Also a good (or top) quality digital piano renders sound of a half pedal different than a full pedal.
Of course, exposing advanced settings for users is very welcome (even 2 separate settings: one for pedal start delay and one for pedal stop delay, both +/-)

In reply to by hstanekovic

Having different settings for pedal start and end would be great.
I believe that it makes sense for a pianist to release the pedal slightly after the end of the note. But this is because he can also create a slight gap between the notes. Musescore also creates slight gaps between notes bit the ticks are not falling there. And midi language is a set of events that are converted to sound. One tick earlier or later can make an event fall in the wrong place.

In reply to by fernandoamartin

> I believe that it makes sense for a pianist to release the pedal slightly after the end of the note. But this is because he can also create a slight gap between the notes.
Yes this is a way to achieve a perfect legato on piano. I should say that before, because if you do not want a perfect legato then this is not a way to do it.

The other problem in MuseScore (naturally occurring in any music notation software) is that you can put events only on note positions (unlike DAW software where you can put them at any moment of time). So in MuseScore, if you want to to end a pedal, for instance, a sixteenth before it's written, you have to enter a hidden sixteenth rest in a new voice and position pedal end on it. It would be great to be able to move playback of pedal (and perhaps some other symbols like crescendo) on any rhythmical position relative to the note they are attached to. Like note position + 1/16 + 1/32 (user should be able to enter "+1/16+1/32" or any other expression using fractions and MusesScore can evaluate this expressions).

In reply to by hstanekovic

There are some symbols that have properties that allow something like that. For example the arpeggio allows fine control on how it will be played. The piano roll also allows some degree of fine control. What would be great was if we could do it to tbe playback any other symbol, like the pedal and if we could insert other customized midi controllers besides CC 64.
Unfortunately in MU4 they are removing many more customizable options that were available in MU3. My hope would be if Jojo had the time to add changes like that to his MU evolution fork.

In reply to by fernandoamartin

Yes, I expected too that MS4 will address such things (matters of fine tuning of the playback) but it went in another direction (orchestral).
Also, I would like to see an ability to select a segment of music and then open its "playback only" equivalent, shown like empty ossia staves where you can write how you want to play things exactly. For instance, you could use complex rhythms to write down exactly how you want to play your arpeggio from slower to faster or with a first note accented etc. (or any other fine details which would result in a too complex/unreadable score). And that in any moment you could show or hide this "playback only" with a single click. Perhaps something like that can already be done, only not in a so convenient way. I should find some time to check this.

Do you still have an unanswered question? Please log in first to post your question.