Features Help Download

Lomse Hacking Guide

3.4. Layouting scores

3.4.1. Introduction and background information

Layouting a score is a very complex task. When I started with Lenmus project I had no experience on anything related to music. I wasn’t able to find academic papers on the subject as all academic knowledge is not free but paywalled and quite expensive. As, normally, you have to read many papers before having a clear picture and finding something usefull, it was totally out of my budget. So, I decided to reinvent the wheel: being optimistic I decided to see it as a great opportunity to learn first hand about the problems involved.

Important

Be awared: all score layouting discusion is based on my own ideas, without any knowledge of academic papers on the subject, appart from GUIDO thesis [1] that is available for free on internet. Probably, many ideas presented here are naif and there are better, documented ways of doing things.

Before starting the description of the scores layouting process, just a few words about the approaches followed and the main problems found:

  • Initially (LenMus 1.x & 2.x) no knowledge at all. My layouting approach was that of a text processor: layout symbols in sequence as if they were charaters in a sentence. No graphical model: all info in the internal model. Two single pass phases: layout and drawing. During layout phase (single pass), positions for objects were computed and stored in the internal model (problems to have two views of the same score). Drawing was just redering symbols at computed possitions. Discovering that time was the key concept for deciding on position.

    Soon it was clear the need of do layouting in two phases: the final positions of some objects (i.e. beams) can not be computed until all notes positions are definitly computed and systems are justified. For layouing, notes and rest are the key symbols that should be layouted first and determines the coarse layout. All remainder symbols are just “decoration” but they can affect to notes/rest positions, although in small degree.

    System justification based on measures (bars). Problems for layouting multi-metric music.

  • LenMus 3.x & 4.x. GUIDO thesis found. I started to isolate rendering information: first steps towards a graphical model. But: no boxes, only shapes objects created, and shapes stored in the internal model. Again, problems for supporting multiple views. Layout was still the same algorithm in two passes. And system justification was still based on measures, so layouting multi-metric music was not possible. Monolitic, complex code, difficult to understand, maintain and improve was refactored by starting to use a test driven approach (TDD). Starting to separate responsibilities, and have better understanding of staep, task and responsibilities during layouting.

  • LenMus 5.x (Lomse library). New layouting algorithm. Clear separation between internal model and graphical model (multiple views now supported). System justification no longer based on measures but re-designed for supporting multi-metric and no-metric music. Full TDD develpment of the layouting algorithms, nearly from scratch.

In this paper I will document current Lomse ideas and implementation but. when appropriate, I will also describe the drawbacks of previous abandoned approaches.

3.4.1.1. How much knowledge should the program have?

When layouting a score the available information will greately vary from only the musical aspects (i.e. a MIDI file without any information about layouting) to a full description for printing (i.e. a detailed LDP or MusicXML file with all details, including positioning information for every symbol).

The most simple approach (full layouting information present in the score file) would imply giving full responsibility to the user: do not add anything not enterd by the user. It is the same situation in traditional engraving with pen and paper. The scores editor becomes just a drawing program. As an improvement, the program could have knowledge about music engraving, but oriented to check for problems and errors. When the user has finished engraving the score and drawing all details, the user could click on a button to validate the engraving and, perhaps, fix some minor issues.

Once the engraved score is saved in a file, including all positioning information (even system and page breaks) no future needs for user intervention when having to render again the score.

But this simple approach is not enough for Lomse. The Lomse library should be able to produce acceptable results with any input, ranging from the two previously described extremes:

  • The simplest one: all layouting information present in the score file. No layouting phase needed, just renderization.
  • The complex one: no layouting information present in the score file, only musical information. Layouting requires a lot of knowledge and it is a very complex task.

Lomse first objective is supporting the LenMus program. This implies two needs:

  • Supporting exercises on music theory and music reading. For scores created by program (for instace, in music reading exercises scores are synthsyzed by the computer) mainly only musical content is generated and all engraving details must be solved by the computer. In these cases, engraving complexity is expected to be low, as most of the content will be musical, with very few additional notation elements.
  • Supporting score edition. Apart from being required for introducing more complex and advanced exercises, an score editor is by itself a desirable feature for students. But for free score edition it is necessary that Lomse has all necessary knowledge to do the engraving without postioning information. For high quality engraving, required processing time could be long. Probably many loops and progressive refinements, Better suited for a batch process. Probably it would be required another layouting algorithm for ‘finaly quality engraving’ before printing, when all musical content has been introduced and coarse layouting algorithm produces an acceptable result.

3.4.1.2. Lomse score layouting objectives

As my objective is, mainly, an interactive editor for music students and simple scores, pragmatic solutions and ad hoc methods will be preferred, even if they do not solve all cases. High quality automatic engraving is not the main objective but:

  • Lomse should ensure that high quality could be achievable by manually fixing/changing those issues that the program could not fully automate
  • In future, a second layouting algorithm, batch oriented, for high quality engraving, could be developed and included in Lomse.

Lomse will consider high quality engraving as a manual process:

  • The program must help on achieving it by reducing the amount of manual intervention.
  • But pragmatism should be the guide: do not spend too much work to automate something but leave the user the task of fixing program output.

3.4.2. Requirements

Initially, three requirements were established:

  1. Automatically breaking music into systems and pages.
  2. Support multi-metric and no-metric music: System breaking should not be based on measures.
  3. Incremental layout, for better performance in long scores.

In following paragraphs I elaborate more on these requirements and comment on current Lomse achievement level.

3.4.2.1. Breaking music into systems and pages

Quoting from GUIDO (p.95):

Three essential tasks must be performed when setting music:

  1. Deciding where to break the lines of a score. This is called line breaking.
  2. Distributing the space between graphical elements of a single line of music. This is called spacing.
  3. Connected to line breaking is the task of distributing the lines of a score in such a way that all printed pages are full. A traditional score usually ends at the end of the last page. The problem of finding line breaks so that all pages are full is called page filling.

Current Lomse algorithms only deal with line breaking and page breaking, but not with page filling. Nevertheless, the algorithms are ready to take page filling into account and it is mainly improving the page break algorithm to take page filling into account.

3.4.2.2. Not based on measures

It should be possible to split a long measure in two or more systems. Scores without barlines (no-metric music), such as many from Eric Satie, should be layouted without problems with no need for the user to do special actions, such as having to insert hidden barlines.

Multi-metric music, that is scores with several instruments in with two or more time signatures are used simultaneusly (i.e. Don Giovanni opera, from Mozart, in the minuet in the first act Finale, No.13) should be also automatically layouted by Lomse without requiring tricks such as inserting hidden barlines.

Current Lomse algorithms fully support multi-metric and no-metric music.

3.4.2.3. Incremental layout

When the score is updated it should be posible to re-layout only the affected measures, without having to re-layout the whole score.

Current Lomse algorithms are not fully incremental. Some mechanisms are in place but are not fully developed.

3.4.3. Some terminology

Before describing the approach let me define some concepts and terminology I will use in this document and in code:

Segment
In a score with only one staff, a system is composed by segments. A segment is any fragment of a system. It is, normally, equivalent to a measure, but in some cases, such as scores without barlines (no-metric music) or with very wide measures not fitting in paper width, a measure can be breaked into two or more segments.
Column
It is the equivalent to a segment, but for scores with several instruments. That is, a column is a vertical slice of a system. Normally a column corresponds to a whole measure but, again, for long measures or in other cases, a column could be only a part of a measure.

Segment is a concept that I will use when refering to one staff and column is the same idea but in reference to a system. The concept of column/segment, as replacement for measure, is the basis for layouting multi-metric music. But do not worry too much, think of column/segment as if it was a measure.

Prolog
Clefs and key signatures are always rendered at start of a system. Time signatures also at start of first system. I will call this fixed part that must be layouted at start of every system the system prolog or simply, the prolog.
Context
I will use the term context for referring to the clef, time signature and accidentals applicable to a given note. The context depends not only on the prolog clef, key and time signature, but also on posible changes introduced before the system start and the note, as well as on accidentals introduced by previous notes in the same measure.

3.4.4. The algorithm: basic approach

3.4.4.1. Layouting approach

To design the layouting algorithm I started considering the following theoretical approach:

  • Layout the score in a single infinite length system. Layout only clefs, keys, times signatures, rests, and noteheads for notes. The idea is to get a coarse layout of the score to be used as the starting point. Therefore, no need to spend time and computational resources in layouting stems, flags, and all other notational objects.
  • Apply the column break algorithm to split the system into columns (system slices).
  • Apply line break algorithm to decide system breaks. Create systems and justify them.
  • Once systems are justified, proceed engraving all the details (all auxiliary notation objects: stems, flags, beams, tuplets, attachments, dynamic mark up, etc.). They will not alter decisions on systems breaks and justification (system lenght) and only will affect to fine notes/rest positions. Finally, determine minimum systems spacing.
  • Apply page break algorith and decide page breaks. Create pages. Page breaks do not alter auxiliary objects engraving, as a page break implied a system break. Therefore, all relation auxiliary objects engraved along two or more systems, are already splitted before the page break (this is not fully true, but it is not complex to deal with the exceptions as we will see later).

3.4.4.2. Delayed engraving of auxiliary objects

As commented, the idea is to start layouting only relevant staff objects (all ImoStaffObj objects) and engrave details once columns has been created and systems are justified. I will justify here this idea.

In previous versions of the layouting algorithm (LenMus version lower than 5.0), all objects where initially engraved (shapes created) and, later, during vertical alignment of notes and rests and systems justification, all shapes had to be re-positioned or even re-computed. Consider, for instance, the problem of having to split a tie at a system break. If the tie shape is created when the note shape is created, there will be not enough information to decide if the tie must be splitted, because system breaks are not yeet computed. Therefore, the approach of creating all shapes at begining required a lot of work for re-considering shapes and repositioning them. To improve performance, two mechanisms were implemented:

  1. A linking mechanism between shapes. Related shapes were attached one to the others, and they received notifications when one of then was moved, so that all other linked shapes could maintain positioning constrains relative to the moved one. [TODO8]: In current code, the linking mechanism between shapes is still in place but it is not used. I’ve decided not to remove it until confirming that it is not really necessary for score edition (although I already have strong fillings about it). It should be removed when finally confirmed. See Delayed engraving of auxiliary objects.
  2. Auto-reshaping shapes, that is, shapes whose layout is not fully defined until they are going to be rendered. At that time, they must compute its final layout. The drawbacks of this approach are that shapes must have some knowledge about engraving. And that they also have to store information about all other objects to take into account for properly positioning and engraving the shape.

Both approaches were costly and required to place a lot of engraving knowledge on the shapes, which is contrary to the single responsibility design principle [2]. So these mechanisms were abandoned in Lomse algorithms.

In current Lomse algorithm, during column creation only ImoStaffObj objects are taken into account. Current algorithm first creates shapes for ImoStaffObj objects. Later, when the columns are created and systems are justified, the process of engraving all remaining objects (the ImoAuxObj objects) takes place.

But engraving ImoAuxObj objects representing relationships between two or more ImoStaffObj objects (i.e. an slur, it relates several notes/rests) can not be done until the last ImoStaffObj of the relationship (i.e. the last note of the slur) is in a system. For instance, a slur across two or more systems can only be engraved only when all affected systems are created. Therefore, engraving ImoAuxObj objects representing relationships between two or more ImoStaffObj objects must be further delayed until the last object of the relation is included in a system.

3.4.5. The layouting algorithm implementation at high level

The algorithm for layouting scores is, conceptually, a five steps process:

  1. Create the columns: Decide which notes/rests will form a column, create the box for it, create the shapes for the staff objects it contains (but do not try to position them) and add them to the column box.
  2. Layout each column: Determine the positions for the shapes, that is, apply the spacing algorithm to set up the minimun distance between notes/rests. Adjust those positions (incrementing space) to align, vertically, notes and rests at the same time positions.
  3. Create the systems: Apply the line break algorithm to determine the columns that will form each system. Add the assigned columns to each system.
  4. Fill pages: Once the score is splitted in systems, apply the page break algorithm to determine the systems that will form each page. Then, systems are added to each page.
  5. Finally, engrave the details.

To implement the sketched process the implementation follows the layout architecture described in section Layouting a document: the algorithm. An specialized object, ScoreLayouter, takes the responsibility for layouting a score. As with any other layouter, the whole algorithm is driven by two methods: prepare_to_start_layout() and layout_in_box().

The first two previously commented steps (create the columns and layout each column) are performed in the preparation phase (prepare_to_start_layout() method). The other three steps (create the systems; fill pages; and engrave the details) are done in the page filling phase (layout_in_box() method).

Here is the algorithm at high level. I will explain later, in detail, each step:

Preparation [prepare_to_start_layout()]:

    * Score layouter creates instrument engravers [create_instrument_engravers()]
    * instrument engravers decide systems indentation [decide_systems_indentation()]
    * instrument engravers are requested to assign height for system
      and position for staves.
    * Score layouter creates column layouters
    * Score layouter asks ColumsBuilder to create the columns.
    * To create a column:
        * create_column_boxes():
            * create_slice_box()
            * for each instrument, create a slice:

        * find_and_save_context_info_for_this_column();
        * collect_content_for_this_column():
            * explore staff objs collection and engrave column
              staff objects (but they are not added to any box)
        * Layout column (only staff objects) [layout_column()]



Page filling [layout_in_box()]:

    * When score layouter is asked to fill one page:
        * If it is first page, apply the line break algorithm to decide on
          line breaks. Break points are stored.
        * Enter in a loop to create systems until the page is full.

    * To create a system:
        * create and save a system layouter
        * create a system box [create_system_box()]
        * fill system with columns until next break point
        * justify_current_system();
        * engrave_system_details();
            * Score layouter engraves Aux Objs for current system
        * instrument engraver is requested to engrave staff lines, name/abbrev
          and brace/bracket.
        * add_initial_line_joining_all_staves_in_system

This algorithm (algorithm-2) is in use in Lomse since SVN rev.25 included. The initial approach for line breaking (see Algorithm 1 for score layouting) was a first-fit: line breaks were decided as systems are set, by filling a line until no more columns could be added. Algorithm 1 was the algorithm used in LenMus (until version 4.3 included) and in Lomse (up to SVN rev.23 included). It was modified and transformed in the described algorithm (algorithm-2) because algorithm-1 did not justify the last system: breaking a line when no more columns can be added doesn’t allow for any optimization, and there are great chances that last system is too short to be justifiable. In current algorithm-2, all columns are measured before deciding where to introduce the line breaks. By knowing in advance all columns’ sizes, line breaks can be optimized for ensuring that the last system is of similar size than all others.

3.4.6. The layouting algorithm implementation in detail

(To be continued ...)

3.4.7. To Do

[TODO8]In current code, the linking mechanism between shapes is still in place but it is not used. I’ve decided not to remove it until confirming that it is not really necessary for score edition (although I already have strong fillings about it). It should be removed when finally confirmed. See Delayed engraving of auxiliary objects.

3.4.8. References

[1]Kai Renz, “Algorithms and Data Structures for a Music Notation System based on GUIDO Music Notation”, PhD Thesis, Darmstadt University of Technology, 2002. Available online from http://www.noteserver.org/diss_kai_final/diss_final.pdf.
[2]Robert C. Martin, “Agile Software Development: Principles, Patterns, and Practices”, 2002. Chapter 9, “The Single Responsibility Principle”. This chapter is available online from http://www.objectmentor.com/resources/articles/srp.pdf.