Lomse Hacking Guide
These are the objects that define the score model:
ImoObj
|
+-- ImoSimpleObj Just contains other objects or
| | properties
| |
| +-- ImoContainerObj A container for ImoContentObj
| |
| +-- ImoInstrument A container for ImoStaffObj
|
+---ImoContentObj
|
+-- ImoBoxLevelObj A box-level object
| |
| +-- ImoScore A container for instruments
|
+-- ImoScoreObj Content for scores
|
+-- ImoStaffObj Main music content objects
|
+-- ImoAuxObj Modifiers. Can go attached to any score object
|
+-- ImoRelObj Relations. Relates two or more StaffObjs.
The approach followed has been to capture the score structure by splitting the representation in containers and content objects. For example, we could imagine the staff as a container for notes and rests.
There are only two container classes: the score (ImoScore object) and the instrument (ImoInstrument class). The score is a container for instruments, and an instrument is a container for the objects representing the music for that instrument. But although bot, score and instrument, are container classes, there is an important difference between them: the score is a box level object, that is, it is a constituent block for document content. But not the instrument, which is just an auxiliary collection of objects. Because of this reason, score and instrument are in different places in the hierarchy. ImoScore derives from ImoBoxLevelObj and ImoInstrument from ImoContainerObj.
All other score related objects are considered content objects, and are modeled by class ImoScoreObj. All these objects that can be included in a score are classified in two main groups: staff objects (ImoStaffObj class) and auxiliary objects (classes ImoAuxObj and ImoRelObj).
An ImoInstrument is, basically, a collection of ImoStaffObj objects. You can think about staff objects as those symbols that are placed on the staff and are the basis for describing the music: I am are referring to symbols such as notes, rests, clefs, time and key signatures, barlines or other symbols with similar characteristics: objects that must me placed on the staff, are ordered by time and are essential to describe a melodic line (see: ImoStaffObj vs. ImoAuxObj: implementation criteria). But, in practice, instead of adding the ImoStaffObj nodes as direct children of ImoInstrument, an auxiliary node ImoMusicData is created. This does not cause any performance or memory burden, facilitates the access to contained staff objects, and mimics the LDP structure, where all staff objects are defined inside a ‘musicData’ tag:
ImoScore
|
ImoInstrument
|
ImoMusicData
|
+------------+------------+
| | |
ImoClef ImoNote ImoNote
All other objects in the score that are not ImoStaffObj objects are considered auxiliary objects, They are like modifiers describing additional properties or adding some editorial highlight on something. For instance, a fermata modifies the duration of a note. A tie modifies the two tied notes by joining their durations, And a title, adds editorial information to an score.
There are two types of auxiliary objects. The first group is formed by those auxiliary objects representing additional properties or modifiers for the owner object (one-to-one relations). For instance, a fermata modifies the duration of the note owning the fermata. These auxiliary objects will be named simple auxiliary objects and will be modeled by objects derived from class ImoAuxObj. They are included in the internal model just as children nodes in the objects they modify. For instance, a fermata in a note is modeled by a ImoFermata child node, whose parent is the ImoNote object. Or the title for an score is an ImoAuxObj attached to the ImoScore. The following example illustrates this:
In LDP this score is written as:
(score (vers 1.6)
(instrument
(musicData
(clef G)
(n g4 q)
(n c5 q (fermata above))
(barline)
)
)
)
And it will create the following internal tree:
ImoScore
|
ImoInstrument
|
ImoMusicData
|
+------------+------+-----+------------+
| | | |
ImoClef ImoNote ImoNote ImoBarline
|
ImoAttachments
|
ImoFermata
Notice that, in practice, ImoFermata is not a direct child of ImoNote. Instead, an intermediate container node ImoAttachments is created. This is just to keep together all ImoAuxObj objects attached to a parent node.
ImoAuxObjs can be attached to any score object. For instance, a title to an score, And also to other ImoAuxObj objects. For instance a text notice attached to a fermata that, in turn , is attached to a note.
The other group of auxiliary objects is formed by those objects modeling a relationship between two or more staff objects. For instance, a tie, and slur, or a dynamics wedge. These auxiliary objects are modeled by abstract class ImoRelObj.
ImoRelObj objects can not be included in the internal tree as children of the staff objects that are part of the relationship, as this would transform the tree into a network. For instance, consider the following score with two tied notes:
In LDP this score is written as:
(score (vers 1.6)
(instrument
(musicData
(clef G)
(n a4 q (tie 1 start))
(n a4 q (tie 1 stop))
(barline)
)
)
)
If the tie is modeled as a child node common to the related notes, it would create the following invalid tree:
ImoScore
|
ImoInstrument
|
ImoMusicData
|
+--------+----+---+---------+
| | | |
ImoClef ImoNote ImoNote ImoBarline
\ /
\ /
ImoTie
For this reason, ImoRelObj objects can not be nodes in the tree model. To solve this problem, they are stored not as child nodes but as internal data in a list of attached ImoRelObj objects. This list is contained in node ImoRelations:
ImoScore
|
ImoInstrument
|
ImoMusicData
|
+-------------+-----+-------+---------+
| | | |
ImoClef ImoNote ImoNote ImoBarline
| |
ImoRelations ImoRelations
\ /
--- --- --- --- --- --- --- --- --- ---- ---
\ / Not part of the tree. Stored
ImoTie as object data
So, simple auxiliary objects (ImoAuxObj) are inserted as children in a ImoAttachments child node, and relation auxiliary objects (ImoRelObj) objects are stored as internal data in a ImoRelations child node:
|
+-----------+-----------+
| |
ImoNote ImoNote
| |
+---------+---------+ ImoRelations
| | |
ImoAttachments ImoRelations |
| \ /
ImoFermata - --- --- --- --- --- ---- ---
\ / Not part of the tree
ImoTie
In any relationship you will find two types of data:
Objects derived from class ImoRelObj represents a relationship, but they only contains the first type of data: the relationship properties and information that is common to all participants.
For modeling the specific data about each participant, another base class is used: class ImoRelDataObj. For instance, to model a group of tree beamed notes, the following objects are created:
Each data object is associated to each participant object (i.e. the notes in the beam) by creating a std::pair object. And all these pairs are stored in the ImoRelObj in a list of participants. Thus, the internal structure for any ImoRelObj is the following:
ptrs. to participants: ImoStaffObj ImoStaffObj
^ ^
ptrs. to participants' | |
data (i.e. its role): | ImoRelDataObj | ImoRelDataObj
| ^ | ^
| | | |
+- -- --|-- --|-- -- -- -- --|-- --|-- -- -- ...
List of | first| |second first| |second
participants -----------> std::pair ---------> std::pair -------> ...
|
|
+- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ...
ImoRelObj
In some relations, there is no specific data to store in the ImoRelDataObj objects. In these cases no ImoRelDataObj` is created, and the internal pointers in ImoRelObj contains NULL.
Here are the objects involved in modeling currently implemented relationships:
ImoRelObj ImoRelDataObj
| |
+-- ImoBeam +-- ImoBeamData
+-- ImoChord | no chord data
+-- ImoTie +-- ImoTieData
+-- ImoTuplet +-- ImoTupletData
+-- ImoSlur +-- ImoSlurData
ImoSpacer is an ImoStaffObj whose purpose is to add more space between the staff objects than precede and follow the ImoSpacer. That is, it is like the space character in text processing.
But ImoSpacer plays also an important role: it is the anchor object for attaching auxiliary objects to an staff. As ImoAuxObj objects are not ImoStaffObj they can not be included directly as content for an staff. Remember that ImoAuxObj objects can only be attached to staff objects. To avoid creating artificial dependencies (attaching auxiliary objects to notes or rest that have nothing to do with the attached object) it is better to attach the auxiliary object to a ‘neutral’ staff object with no meaning, an anchor object: an ImoSpacer of zero width.
When having to implement a new notation object, sometimes it is difficult to decide if it should be an ImoStaffObj or an ImoAuxObj. The best approach is to consider how the music line (melody) is affected by the presence of the new notational object:
In summary, the key rule is use ImoStaffObj for measure-attached objects that should affect all objects coming after it. On the contrary, use ImoAuxObj/ImoRelObj for note-attached objects that should affect only to the parent objects.