Closed adrianholovaty closed 1 year ago
In Calliope, clefs (and other non-timed elements) are simply in sequence on the staff in with the notes, barlines, etc. Every element has a voice ID, but it is also possible for an element such as a clef or other signature to have a list of voice IDs, that are used to show one clef that applies to multiple voices on the staff. The system formatting algorithm places the clef in the right place relative to the other elements, and the performance algorithm can tell that the clef applies to certain voices. So clefs aren’t duplicated, and they don’t have a timed location, and this could be represented by the proposed StartEvent ID. But it would be easier not to make it exceptional, but simply put it in the sequence of elements on the staff without needing the StartEvent? On 24 Sep 2023, at 22:05, Adrian Holovaty @.***> wrote: How should inner-bar clef changes be encoded? More precisely, how should the "position" of a positioned-clef object be encoded? Some requirements:
Using a rhythmic position in the bar is the simplest solution, but that doesn't handle the case of clef changes within a run of grace notes. (Grace notes all have the same rhythmic position.) In a multi-staff part, a clef should be able to be associated with a specific staff. In multi-voice music, a clef should be able to be associated with a certain voice. See this comment and this comment for examples. In multi-voice music, it would be nice not to need to encode an inner-bar clef change multiple times (once per sequence), though that may be unavoidable.
See #312, #96 and #45 for prior discussion. The most complete solution so far is @clnoel's offhanded proposal from this comment:
I would almost rather that the clef be given an "appears before note-reference-id" location, rather than a timed location.
Just to kick off a fresh conversation, here's how that might look: { "clef": {"line": 2, "sign": "G"}, "startEvent": "id_of_event" } ...where id_of_event would be the "id" of the first event object that the clef applies to. In multi-voice music, this would need to be encoded twice, though — which is a bit inelegant. Any thoughts or other ideas?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: @.***>
If it's helpful, after years of dealing with problems of how to ensure elements appear in their "proper" order, music21
ended up specifying all musical objects with a six-element list/tuple, which has ended up serving the project well for the past decade-plus:
[atEnd=[0,1], offset=rational_number, priority=int, classSortOrder=int, isNotGrace=[0,1], insertIndex=non_negative_int]
languages that allow for <
and >
comparison of lists/tuples of the same length of numbers can sort elements based on this information.
atEnd
- objects that are placed at the end of the container no matter what else is placed in the container (like end barlines, cautionary clefs, etc.offset
- we measure in quarter note units. Rational number is ideal, but floats can be used for non-tuplets for speed.priority
- nearly always 0
, but a way of manually manipulating the order of elements at the same offset. Important for grace note grouping.classSortOrder
- an ordinal number assigned to each type of musical element to specify how they're ordered. Clefs have lower sort order than keys which are lower than meters which are lower than notes, etc.isNotGrace
- the awkward negative assures that grace notes sort before the notes at the same time. Could also be achieved by giving grace notes/chords/rests their own classes with lower classSortOrder than notes.insertIndex
- a tie-breaker for consistency -- just a continuously increasing index of the order that objects were created.Anyone who hasn't developed a full-fledged system will find this overkill, but over time something with this level of complexity always seems to be created. :-)
After thinking about this, here's an expansion on my off-hand proposal:
{
"clef": {"line": 2, "sign": "G", "staff": 0},
"startEvents": {"event-id-1", "event-id-2"}
}
Notable here:
staff
to this, which can be either required, or optional and assumed to be 0 if not specified.
startEvents
startEvents
as an optional field to be multiple events.
startEvents
is absent, the clef is assumed to be at the start of measure, to apply to all sequences in the staff, and to carry over to the next measure.I believe this addresses the problem of not wanting to specify one visible object multiple times, while also allowing the clef to apply to all voices on a staff or one voice on a staff.
The only time this gets complicated is when we need to decide if a clef carries over to the next measure, if it was applied to just one sequence.
--Christina
Edited to add: Just had a thought... We might allow events sequences in following measures to be in the "startEvents" field if the clef carries over for one voice only. The clef would still be placed horizontally before the earliest of the start events, so would not be repeated at the next measure.
@clnoel Nice, thanks for giving this thought and expanding the idea!
Just to make sure I understand startEvents
: the purpose is to handle multiple voices, right? So in case of a measure with two voices (a part-measure containing two sequences), startEvents
would contain two event IDs, one for each sequence? I think this is what you meant, but I got confused by "placed horizontally before the earliest" (emphasis mine). Let me know if I'm misunderstanding here.
Also, I think the "staff"
should go directly on this structure, not within the "clef"
. This keeps the "clef"
object nice and pure — it represents only an abstract clef, without a position.
@adrianholovaty
Maybe I should have said "leftmost" instead of earliest.
As an example:
In the above picture, both the down-stem grace and the up-stem half note would be in the startEvents
array, and the clef would position itself to the left of the leftmost note (the grace note), regardless of the order that they are listed in the startEvents
array.
Also, I included staff
in the clef definition because line
was already there. If those are conceptually different vertical placement definitions, then I don't mind splitting it out.
Just to note: I've opened a separate issue, #314, for the more general problem of addressability for notes (including grace notes). I think once we have a solution there, it'll be reasonably easy to do inner-bar clef changes.
This is now done, via the new "rhythmic position" object:
https://w3c.github.io/mnx/docs/mnx-reference/objects/rhythmic-position/
To see it in action, see the new clef changes example document. The inner-bar clef change is encoded with that rhythmic position:
https://w3c.github.io/mnx/docs/mnx-reference/examples/clef-changes/
This uses the new concept of a "positioned clef", documented here:
https://w3c.github.io/mnx/docs/mnx-reference/objects/positioned-clef/
How should inner-bar clef changes be encoded? More precisely, how should the "position" of a positioned-clef object be encoded?
Some requirements:
See #312, #96 and #45 for prior discussion.
The most complete solution so far is @clnoel's offhanded proposal from this comment:
Just to kick off a fresh conversation, here's how that might look:
...where
id_of_event
would be the"id"
of the first event object that the clef applies to. In multi-voice music, this would need to be encoded twice, though — which is a bit inelegant. Any thoughts or other ideas?