-
Notifications
You must be signed in to change notification settings - Fork 23
Understanding libmei
The ODD meta-schema language makes use of some grouping mechanisms to allow elements with similar functions to inherit similar attributes. For example, both the note
and rest
elements would need identical attributes for dealing with rhythmic values, while only note
would need attributes for dealing with pitch values.
In LibMEI, these attribute groups are expressed as "mixins." Mixins on elements allow programmatic access to attribute getters, setters, and deleters. Let's look at a simple example. For this section we will refer to our doxygen documentation, just so you can get a feel for how it is organized.
mei::Note
has a large number of mixins associated with it. Let's look first at the PitchMixIn. This class defines four methods:
MeiAttribute * getPname ()
void setPname (std::string _pname)
bool hasPname ()
void removePname ()
This is made available as a "Public Member" of the mei::Note
class. Each mixin class is instantiated when the MeiElement class is instantiated. For this example, our mei::Note
class has the following constructor (truncated for clarity):
mei::Note::Note() :
MeiElement("note"),
/* other mixins */
m_Pitch(this),
/* other mixins */
{
}
And the corresponding header declaration (also truncated):
class MEI_EXPORT Note : public MeiElement {
public:
Note();
Note(const Note& other);
virtual ~Note();
/* other methods and mixins */
PitchMixIn m_Pitch;
/* other methods and mixins */
};
This then makes the members of the PitchMixIn available to the mei::Note
class via an instance of it. This makes the following possible:
Note* n = new Note();
n->m_Pitch.setPname("c");
MeiAttribute* a = n->m_Pitch.getPname();
bool b = n->m_Pitch.hasPname(); //returns 'true'
n->m_Pitch.removePname(); // deletes the pname attribute
n->m_Pitch.hasPname(); //returns 'false'
While many methods are defined in mixins, a few are defined on the elements themselves. If attributes do not need to be shared across elements, it makes more sense to store them directly in the element definition. This means that some methods do not use the mixin pattern shown above.
For example, the mei::Hand
object has the following non-mixin methods:
MeiAttribute * getInitial ()
void setInitial (std::string _initial)
bool hasInitial ()
void removeInitial ()
These non-mixin methods are called much like a regular member function:
Hand* h = new Hand();
h->setInitial("true"); // remember, we're setting strings not booleans!
bool b = h->hasInitial(); // returns an actual boolean, 'true'
h->removeInitial();