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

Inner-bar clef changes #313

Closed
adrianholovaty opened this issue Sep 24, 2023 · 7 comments
Closed

Inner-bar clef changes #313

adrianholovaty opened this issue Sep 24, 2023 · 7 comments

Comments

@adrianholovaty
Copy link
Contributor

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?

@williamclocksin
Copy link

williamclocksin commented Sep 24, 2023 via email

@mscuthbert
Copy link

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. :-)

@clnoel
Copy link

clnoel commented Sep 25, 2023

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:

  • Added staff to this, which can be either required, or optional and assumed to be 0 if not specified.
    • The clef is placed vertically according to staff and line regardless of what is specified in startEvents
  • Made startEvents as an optional field to be multiple events.
    • The clef is placed horizontally before the earliest of the start events.
    • For each sequence containing a start event, the notes in the sequence following the start event are affected by the clef for the purpose of placement on the staff, until there is another clef that affects that sequence or the end of the sequence.
    • If 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.

@adrianholovaty
Copy link
Contributor Author

@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.

@clnoel
Copy link

clnoel commented Sep 25, 2023

@adrianholovaty
Maybe I should have said "leftmost" instead of earliest.
As an example:
image
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.

@adrianholovaty
Copy link
Contributor Author

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.

@adrianholovaty
Copy link
Contributor Author

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/

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

4 participants