Closed Leon-Focker closed 8 months ago
Ah yes, I've encountered this before. I think there is perhaps a deeper issue here, how does one attach a mark to an event that's not the first in a long note or rest?
In my case it's this method with an added (add-mark event mark).
It's something certainly intended--a bar with only a bunch of rests would be turned into a whole-rest bar in normal Western notation--but I could see there might be cases where its desirable to have the occasional mark over a rest. It should be an easy fix to be honest, but can you send some code that creates then deletes such marks when rendering a score (just to be sure we're on the same page)?
Sure, here is a simple example:
(let* ((rthms '(w h q q w h h))
(pitches '(e4 nil nil nil e4 e4 e4))
(events '())
(bars '())
(ate 0)
(bar nil)
(sc nil))
(setf events (loop for r in rthms and p in pitches collect (make-event p r)))
(add-mark (first events) 'cresc-beg)
(add-mark (third events) 'cresc-end)
(add-mark (fourth events) 'dim-beg)
(add-mark (seventh events) 'dim-end)
(add-mark (third events) "do stuff")
(add-mark (sixth events) "do stuff")
(loop while events do
(setq bar (make-rthm-seq-bar '((4 4)))
ate (fill-with-rhythms bar events)
events (when ate (nthcdr ate events)))
(push bar bars))
(setq sc (bars-to-sc (reverse bars)))
(write-xml sc))
Currently that produces this in musescore:
Yes, I think the fix would be quite simple. On my branch I just did this in the #'force-rest-bar method:
(let* ((marks (flatten (mapcar #'marks (rhythms rsb)))))
...
(marks new) (remove-if #'mark-for-note-only marks) ; (marks first))
And now this is notated:
I would still generally caution against marks on rests in bars which only have rests. Your example is a good reason to be careful: what exactly does a crescendo over a rest mean? It's impossible right?
Anyhow, that's a good suggestion and though I didn't understand where you were doing that at first, I've implemented what I think you mean and all the tests pass so I think we can incorporate this into the main branch.
Essentially what we're now doing is bunging all the marks--excluding those that can only be attached to a note--onto the whole-bar-rest. It won't help if the granularity of the rests is important, and it won't really help definitively with hairpin dynamics as these are a nightmare in any software I've ever used (including Dorico), but this is still helpful I suppose.
BTW I wasn't seeing all your events with your code so I made 2/4 bars instead:
(let* ((rthms '(h h q q h q q))
(pitches '(e4 nil nil nil e4 e4 e4))
(events '())
(bars '())
(ate 0)
(bar nil)
(sc nil))
(setf events (loop for r in rthms and p in pitches collect (make-event p r)))
(add-mark (first events) 'cresc-beg)
(add-mark (third events) 'cresc-end)
(add-mark (fourth events) 'dim-beg)
(add-mark (seventh events) 'dim-end)
(add-mark (third events) "do stuff")
(add-mark (sixth events) "do stuff")
(loop while events do
(setq bar (make-rthm-seq-bar '((2 4)))
ate (fill-with-rhythms bar events)
events (when ate (nthcdr ate events)))
(push bar bars))
(setq sc (bars-to-sc (reverse bars)))
(cmn-display sc))
You can see what I mean about hairpins when you look at the cmn output:
I can definitely think of occasions where one would need marks on a rest without a not. I do a lot of contemporary choral stuff at the moment and there are often instructions for singers to do with movement or gestures or breaths that require marks but not notes. New/augmented instruments might need this too. Andrew McPherson's resonator piano, for example, might require pedal marks over rests.
Ah yes, I see what you mean Michael. I also agree, that a crescendo over a rest does not really make sense. But I would rather have a crescendo over a rest than it extending much further than it should and thus producing something that looks right but is entirely wrong - even if thats just how Musescore then interprets the data. Thats why I chose that example.
Anyhow, I will test your implementation in a bit, thanks in advance!
@danieljamesross There's an easy fix to that, which I've just implemented
(set-sc-config 'ignore-force-rest-bar t)
so no matter where (force-rest-bar) is called, it won't be executed.
E.g.
(set-sc-config 'ignore-force-rest-bar t)
(let* ((rthms '(h q q h h q q))
(pitches '(e4 nil nil nil e4 e4 e4))
(events '())
(bars '())
(ate 0)
(bar nil)
(sc nil))
(setf events (loop for r in rthms and p in pitches collect (make-event p r)))
(add-mark (first events) 'cresc-beg)
(add-mark (third events) 'cresc-end)
(add-mark (fourth events) 'dim-beg)
(add-mark (seventh events) 'dim-end)
(add-mark (third events) "do stuff")
(add-mark (sixth events) "do stuff")
(loop while events do
(setq bar (make-rthm-seq-bar '((2 4)))
ate (fill-with-rhythms bar events)
events (when ate (nthcdr ate events)))
(push bar bars))
(setq sc (bars-to-sc (reverse bars)))
(cmn-display sc))
(set-sc-config 'ignore-force-rest-bar t)
nice!
Everything is working as expected for me, all tests passed. Thanks again.
I'm not sure whether this is bug or feature: When I place a mark on a rest that is not the first event in a bar full of rests, this mark disappears. This is due to the #'force-rest-bar method (or the #'consolidate-rests method, which calls the former), as it only copies marks of the first event in the bar into the new bar. This is probably an edge case when using slippery chicken "normally". But I wonder if discarding those marks has any purpose, or if one could change the method to copy the marks of all events?