Google Summer of Code 2016: cross-voice spanners

This summer, I’ve had the special opportunity to participate in the Google Summer of Code (GSoC) program with Lilypond. To describe GSoC briefly, students worldwide are paid a stipend by Google to work on coding projects for various open source organizations. My project deals with spanners, musical objects that start and end in different places, like slurs and crescendi. Specifically, I’ve been working on allowing users to create cross-voice spanners—spanners that start and end in different voices.

Suppose we want this crescendo to start in the first voice and end in the second voice:

<< { g\< a } \\ { f e d c\! } >>

Of course, this won’t work, since the start of the crescendo never knows about the end—which occurs in a different voice—and vice versa. The current solution to this problem is to end the crescendo in the same voice, using hidden notes:

<< { g\< a s s\! } \\ { f e d c } >>

Although this works, the code doesn’t express our intention well: we want to start a crescendo on the G and end it on the C. Moreover, this workaround can be unwieldy to use in more complicated situations. With the changes I’ve made, however, this result can be achieved with the following clearer code:

<< { g\=Staff.1\< a } \\ { f e d c\=Staff.1\! } >>

The key command here is \=, which sets the spanner id and share context of an event—in this case, the crescendo start event \< and stop event !. As of version 2.18, this command can already be used to write overlapping slurs. Now, we could also use \= just to write overlapping crescendi:

{ c\=1\< d\=2\< e f\=1\! g\=2\! }

In this case, only the spanner id needs to be set, which serves the purpose of indicating which start and stop events to match together. To understand what the “share context” Staff means, however, suppose the snippet from before belongs to a larger score:

<<
  \new Staff { << { g\=Staff.1\< a } \\ { f e d c\=Staff.1\! } \\ { a a\< a a\! } >> }
  \new Staff { << { a b\=Staff.2\< } \\ { g f e\=Staff.2\! d } >>
>>

Although both the first and second Staff have crescendo events, the events in each Staff are processed separately, because Lilypond is only matching start and stop events in the same Staff. In other words, the “share context” indicates the context within which Lilypond looks for matching start and stop events. So in this case, we could have used the same id in both Staffs, since each crescendo is limited to starting and ending in the same Staff.

I’ll briefly describe my general progression in developing this project. Throughout, I often asked questions and discussed ideas with the lilypond-devel mailing list, which was a great help.

  • First, I had to understand the overall process that transforms user input like \< and \! into the objects seen in the final output. This included learning Scheme (which fortunately didn’t take too long), as a lot of the relevant code is written in Scheme. From this, and with some clarifying answers from the the mailing list, I learned why the code limited spanners to starting and ending in the same voice.
  • Next, I brainstormed and tried some different approaches to get Lilypond to connect start and stop events in different voices as part of the same spanner. I chose to experiment with the dynamic engraver, which was simple enough for me to easily understand and try changing. With the help of the mailing list, I eventually settled on an approach that seemed promising. Essentially, when a spanner is started, it may be stored in a context above (the “share context” described earlier), allowing another voice to end it when the stop event is received.
  • Although this did work for dynamics, when I submitted my code to be reviewed, I realized that I would have had to write a lot of similar code for other spanners. To get the dynamic engraver to support cross-voice spanners, I also had to change it to manage multiple spanners at the same time (e.g. overlapping crescendi). However, a lot of the code for doing so resembled already-existing code for the same purpose in the slur engraver. It was therefore suggested that I separate out the code for this, so it wouldn’t be repeated in all engravers that support cross-voice spanners.
  • To implement this, instead of having one engraver in each voice be responsible for multiple spanners, multiple engravers are created that each handle one spanner. Basically, instead of having to rewrite an engraver to keep track of multiple spanners at the same time, I wrote code to make multiple copies of the engraver; since the engraver can already deal with one spanner, having multiple copies effectively allows for multiple spanners to be created simultaneously. The details took some time to work out, but having finished, the dynamic engraver can now create cross-voice spanners with just a few changes needed.

All the code I wrote for GSoC is found on my GitHub fork of Lilypond.

  • Any branch beginning with experimental contains unfinalized, often work-in-progress code that will not directly be included in Lilypond.
  • The gsoc-2016-spanners-old branch has my first attempt at cross-voice dynamics. Although (as mentioned above) this patch ended up requiring more refactoring, it contained a small change that was accepted as part of a different patch. This change has made it into Lilypond (12b68a3) and will be included in version 2.19.48, though not much will visibly change since only the internal representation of spanner id’s was altered.
  • Finally, the gsoc-2016-spanners branch has the code I completed at the end of GSoC; at the time of writing, it is not in Lilypond yet, but has been submitted for review.

I’ve learned a lot about both Lilypond and coding as I worked on this project. Although I was only able to make dynamics and slurs cross-voice during the timeframe—and to clarify, at the time of writing (2.19.47), this is not yet available in any release—I intend to continue contributing even after GSoC is over. Thank you Jan-Peter Voigt, for being my mentor, guiding me, and checking my code; Urs Liska, for helping me find a mentor and with the GSoC application; David Kastrup, for answering many of my questions and reviewing my code; everyone else who helped me on the mailing list; and the Lilypond community for creating Lilypond and providing me with this opportunity this summer.

Jeffery Shivers, another student working on Lilypond for GSoC, will also be posting his experience with GSoC soon. This will be updated with a link once it’s ready.

5 thoughts on “Google Summer of Code 2016: cross-voice spanners

  1. Jay Anderson

    A very useful feature to avoid the contortions we go through to fake this effect. Thanks for the write up and the work.

    Reply
  2. Abraham Lee

    Great work, Nathan, and everyone who assisted him! This will be very helpful! I’m excited to see this functionality make its way into other spanners as well!

    Reply
  3. Christopher Antila

    I’ll echo everyone’s congratulations here. This will be a big step forward for LilyPond when released, and we’re going to wonder how we ever lived without it! Thanks, Nathan.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *