Skip to content

Understanding libmei

ahankinson edited this page Mar 1, 2012 · 14 revisions

Mixins

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'

Member methods

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();