Lomse library. API documentation  0.30.0
The Document API

Accesing and modifying the document

Lomse operates in the domain of music books and music scores. That is, Lomse deals with documents, written in LDP language, containing texts, images, scores, exercises, graphics, headers, footers, etc. Also deals with music scores written in other languages, such as MusicXML.

A file written in MusicXML or LDP languages is an 'external representation' of a music score or a music document, that is, something oriented to be stored in a file. This type of representation is not the most appropriate for processing. Therefore, when a MusicXML or LDP file is read by Lomse, the file content is transformed into a suitable format in memory, more flexible and powerful for processing. This representation is named the Internal Model .

The internal model is accessible for your application through the ADocument object. This object represents a lomse document and provides access to its content, so that your application can interact with the document, get information from it and change things. You can even create the document content from scratch instead of loading its content from an external file.

Until now, this API object didn't exist and to manipulate the internal model you had to access lomse internal objects and understand the Internal Model. The need for an stable API hiding all internal details is clear, to get two main benefits:

  1. The internal model will be really ‘internal’ and could be improved for new features and fixes without affecting user applications.
  2. The library API will be stable, and user oriented, removing all internals.

Therefore, lomse is starting to define and offer a user oriented API for accessing the internal model. This API is in development and, currently, only a few classes and methods are available. Meanwhile you application will continue having access to the lomse internal model classes but this access will be, in future, removed.

Defining and implementing the public API will take its time and will be done little by little. It is strongly recommended to use the new API classes when available instead of accessing internal objects. But, for backwards compatibility, as your application could be using methods not yet moved to the public API, all API classes include a bridge method get_internal_object() that provides access to the most relevant internal model object, so that you can migrate your code and still use internal classes for methods not yet migrated. Notice that this helper method will be removed in future so, please, if you need to use it, open an issue explaining the need, so that the public API could be fixed as soon as possible and your app. would not be affected in future when these backwards compatibility methods are removed.

The ADocument class

The document is accessible through the ADocument class, that represents the whole document. This object can be accessed by method Presenter::get_document(). You could think of the ADocument as the root node of a DOM like structure representing the document. And the children of this root element represent the basic blocks for building a document: headers, paragraphs, music scores, lists, tables, images, etc.

For music scores the DOM model analogy is not truly feasible as many music notation markings (like a slur or beam) are represented by pairs or sets of elements. By using just a tree structure there is no automatic way to ensure consistency between these elements. Therefore, lomse or any other program have to maintain self-consistency when there are 'paired elements' representing the starts and ends of things like beams, crescendo markings, slurs, and so on. Therefore, the Internal Model contains additional structures for correctly representing a music score.

It is important to note that the Internal Model and its associated structures form an abstract representation for a document, containing all necessary information to derive any other representation when needed. It is not oriented to any particular use (rendering, playback, printing, music analysis, document edition, ...) but try to serve all them equally. In this way the Document can serve to the purpose of any View object: a view can either be a print out of a conventional music score; or it can be a textual representation in LDP or any other language; or it can be a playback oriented representation, such a table of MIDI events; or any other representation for any purpose. When a particular use is intended, the corresponding optimized representation is derived from the Internal Model by Lomse.

ADocument content

You can think on a document as a container for the visible content (music scores, paragraphs, etc.) and some additional properties and metadata:

Document := metadata + content
content := collection of <i>block-level</i> objects (paragraphs, music scores, tables, etc).

For structuring the content of a document, lomse follows an approach similar to that of HTML (but not exactly equal), and classifies document content objects in two classes: block-level objects and inline-level objects.

In a lomse, document first level content items are always block-level objects. They define independent areas in a document, such as a paragraph or a music score. block-level objects can be considered as boxes to wrap the content, and they may content other block-level objects and inline-level objects. Generally, block-level objects begin on new lines, whereas inline-level objects do not.

Inline-level objects must always be included as part of a block-level object, and may contain only data and other inline-level objects.

In lomse, there are three types of block-level objects:

  1. blocks-container objects: they only contain more block-level objects, e.g.: ATable contains ATableCell and ATableRow objects.
  2. inlines-container objects: they only contain inline-level objects, e.g.: AParagraph contains ATextItem objects.
  3. music score objects: an specialized blocks-container for containing a music score (class AScore).

As you can see, document content is organized in a tree, and the first level nodes are always block-level objects. For example, let's analyse the structure of a real document, the following LMD document:

<?xml version="1.0" encoding="UTF-8"?>
<lenmusdoc vers="0.0" language="en">
<content>
<section level="1">1. A simple music score</section>
<para>
The next example is just a simple music score.
It only has one staff, a G clef, a key signature (A major)
and two notes: a quarter note and an eighth note.
</para>
<ldpmusic>
(score (vers 2.0)
(instrument
(musicData
(clef G)
(key A)
(n a4 q)
(n c5 e)
)
)
)
</ldpmusic>
<para>Do you like it?</para>
</content>
</lenmusdoc>

will be is rendered by Lomse as:

document-example.png
Image: Rendering of previous LMD document.

And from the API point of view you can think of this document as a tree represented by the following objects:

                              ADocument
                                  |
         ┌────────────────┬───────┴────────┬──────────────────┐
         |                |                |                  |
      IHeading       AParagraph         AScore           AParagraph
         |                |                |                  |
     ATextItem        ATextItem           ...             ATextItem


.        

As you can see, the API tree follows, basically, the same structure than that of a LenMus document (LMD) and an score follows the same structure than its LDP source.

For content other than music scores the structure is similar to that of an HTML document. But for music scores there is some additional information added to the model. See next section The structure of a music score for details.

The structure of a music score

The AScore object represents a full music score, that is, an object comprising all of the music for all of the players and their instruments, typically laid out in a specific order. In a full score, instruments are usually grouped by sharing barlines and having some visual clues, such as a brace or bracket, and a group name. The visual groupings we see in a piece of music can be classified as:

  • Staff - a set of horizontal lines, where musical notation can be arranged for display.
  • Grand staff - two staves vertically linked by a curly brace, and sharing measure barlines, used for instruments having enough range to be confusing when put on a single staff. (e.g. piano or organ). For some instruments, such as organ, it could have more than two staves.
  • Instruments group - a set of staves or grand staves connected by a curly or square brace, used to indicate that the instruments involved are closely linked (e.g. SATB voices or 8 Horns). Sometimes, all instruments in the group share measure barlines.
  • System - a set of staves, grand staves, and/or grouped staves connected, at start, by a vertical system barline, representing every part in the current layout.

The two first, staves and grand staves, are represented in lomse by an AInstrument object: the staff or staves associated to a physical musical instrument.

The third one, instruments groups, are represented in lomse by AInstrGroup objects: the layout information for grouping some instruments.

Finally, systems, are represented by internal objects managed by lomse.

Thus, in lomse, an score is, basically, some global information such as score titles, and two collections: the collection instruments (AInstrument objects) and the collection of defined instrument groups (AInstrGroup objects).