Fixed: Breve bar rests are now properly recognized

function GenerateMEIHeader () {
    //$module(ExportGenerators.mss)
    // takes in a Sibelius Score object
    // returns a libmei tree (i.e., nested objects and arrays) with a MEI header with metadata
    score = Self._property:ActiveScore;
    header = libmei.MeiHead();
    fileD = libmei.FileDesc();
    titleS = libmei.TitleStmt();
    libmei.AddChild(header, fileD);
    libmei.AddChild(fileD, titleS);

    workDesc = libmei.WorkDesc();
    wd_work = libmei.Work();
    wd_titleStmt = libmei.TitleStmt();
    wd_title = libmei.Title();
    wd_respStmt = libmei.RespStmt();
    libmei.AddChild(wd_titleStmt, wd_title);
    libmei.AddChild(wd_titleStmt, wd_respStmt);
    libmei.AddChild(wd_work, wd_titleStmt);
    libmei.AddChild(workDesc, wd_work);

    title = libmei.Title();
    libmei.AddChild(titleS, title);

    if (score.Title != '')
    {
        libmei.SetText(title, score.Title);
        libmei.SetText(wd_title, score.Title);
    }
    if (score.Subtitle != '')
    {
        subtitle = libmei.Title();
        libmei.AddChild(titleS, subtitle);
        libmei.SetText(subtitle, score.Subtitle);
        libmei.AddAttribute(subtitle, 'type', 'subtitle');
    }
    if (score.Composer != '')
    {
        composer = libmei.Composer();
        libmei.AddChild(titleS, composer);
        libmei.SetText(composer, score.Composer);
        wd_composer = libmei.PersName();
        libmei.AddAttribute(wd_composer, 'role', 'composer');
        libmei.SetText(wd_composer, score.Composer);
        libmei.AddChild(wd_respStmt, wd_composer);
    }
    if (score.Lyricist != '')
    {
        lyricist = libmei.Lyricist();
        libmei.AddChild(titleS, lyricist);
        libmei.SetText(lyricist, score.Lyricist);
        wd_lyricist = libmei.PersName();
        libmei.AddAttribute(wd_lyricist, 'role', 'lyricist');
        libmei.SetText(wd_lyricist, score.Lyricist);
        libmei.AddChild(wd_respStmt, wd_lyricist);
    }
    if (score.Arranger != '')
    {
        arranger = libmei.Arranger();
        libmei.AddChild(titleS, arranger);
        libmei.SetText(arranger, score.Arranger);
        wd_arranger = libmei.PersName();
        libmei.AddAttribute(wd_arranger, 'role', 'arranger');
        libmei.SetText(wd_arranger, score.Arranger);
        libmei.AddChild(wd_respStmt, wd_arranger);
    }
    if (score.OtherInformation != '')
    {
        wd_notesStmt = libmei.NotesStmt();
        ns_annot = libmei.Annot();
        libmei.AddChild(wd_work, wd_notesStmt);
        libmei.AddChild(wd_notesStmt, ns_annot);
        libmei.SetText(ns_annot, score.OtherInformation);
    }

    respS = libmei.RespStmt();
    libmei.AddChild(titleS, respS);
    persN = libmei.PersName();
    libmei.AddChild(respS, persN);
    pubS = libmei.PubStmt();
    libmei.AddChild(fileD, pubS);

    avail = libmei.Availability();
    ur = libmei.UseRestrict();
    libmei.AddChild(avail, ur);
    libmei.AddChild(pubS, avail);
    libmei.SetText(ur, score.Copyright);

    encodingD = libmei.EncodingDesc();
    libmei.AddChild(header, encodingD);
    appI = libmei.AppInfo();
    libmei.AddChild(encodingD, appI);

    applic = libmei.Application();
    libmei.SetId(applic, 'sibelius7');
    libmei.AddChild(appI, applic);
    libmei.AddAttribute(applic, 'version', Sibelius.ProgramVersion);
    nm = libmei.Name();
    libmei.SetText(nm, 'Sibelius (' & Version & ')');
    libmei.AddChild(applic, nm);

    libmei.AddChild(header, workDesc);

    return header;
}  //$end

function GenerateMEIMusic () {
    //$module(ExportGenerators.mss)
    score = Self._property:ActiveScore;

    Self._property:TieResolver = CreateDictionary();
    Self._property:LyricWords = CreateDictionary();
    Self._property:SpecialBarlines = CreateDictionary();
    Self._property:SystemText = CreateDictionary();
    Self._property:LayerObjectPositions = null;
    Self._property:ObjectPositions = CreateDictionary();

    // the section parent is used in case we need to inject
    // a new parent later (e.g., for endings)
    Self._property:SectionParent = null;
    Self._property:VoltaBars = CreateDictionary();
    Self._property:ActiveVolta = null;
    Self._property:VoltaElement = null;

    // grab some global markers from the system staff
    // This will store it for later use.
    ProcessSystemStaff(score);

    music = libmei.Music();
    body = libmei.Body();
    mdiv = libmei.Mdiv();
    sco = libmei.Score();

    libmei.AddChild(music, body);
    libmei.AddChild(body, mdiv);
    libmei.AddChild(mdiv, sco);

    scd = sibmei2.GenerateScoreDef(score);
    libmei.AddChild(sco, scd);

    barmap = ConvertSibeliusStructure(score);
    numbars = barmap.GetPropertyNames();
    numbars = numbars.Length;
    Self._property:BarMap = barmap;

    section = libmei.Section();
    libmei.AddChild(sco, section);

    for j = 1 to numbars + 1
    {
        // this value may get changed by the volta processor
        // to inject a new parent in the hierarchy.
        progressMsg = utils.Format(_ExportingBars, j, numbars);
        cont = Sibelius.UpdateProgressDialog(j, progressMsg);

        if (cont = 0)
        {
            ExitPlugin();
        }

        m = GenerateMeasure(j);
        ending = ProcessVolta(j);

        if (ending != null)
        {
            libmei.AddChild(section, ending);
            libmei.AddChild(ending, m);
        }
        else
        {
            libmei.AddChild(section, m);
        }
    }

    return music;
}  //$end

function GenerateMeasure (num) {
    //$module(ExportGenerators.mss)

    score = Self._property:ActiveScore;
    // measureties
    Self._property:MeasureTies = CreateSparseArray();
    Self._property:MeasureLines = CreateSparseArray();

    m = libmei.Measure();
    libmei.AddAttribute(m, 'n', num);

    barmap = Self._property:BarMap;
    staves = barmap[num];
    children = CreateSparseArray();

    // since so much metadata about the staff and other context
    // is available on the bar that should now be on the measure, go through the bars
    // and try to extract it.

    for i = 1 to staves.Length + 1
    {
        this_staff = score.NthStaff(i);
        bar = this_staff[num];

        if (bar.ExternalBarNumberString)
        {
            libmei.AddAttribute(m, 'label', bar.ExternalBarNumberString);
        }

        s = GenerateStaff(i, num);
        libmei.AddChild(m, s);
    }

    mties = Self._property:MeasureTies;

    for each tie in mties
    {
        m.children.Push(tie);
    }

    mlines = Self._property:MeasureLines;

    for each line in mlines
    {
        m.children.Push(line);
    }

    specialbarlines = Self._property:SpecialBarlines;

    if (specialbarlines.PropertyExists(num))
    {
        bline = specialbarlines[num];

        if (bline = 'rptstart')
        {
            libmei.AddAttribute(m, 'left', bline);
        }
        else
        {
            libmei.AddAttribute(m, 'right', bline);
        }
    }

    systemtext = Self._property:SystemText;

    if (systemtext.PropertyExists(num))
    {
        textobjs = systemtext[num];
        for each textobj in textobjs
        {
            text = ConvertText(textobj);

            if (text != null)
            {
                libmei.AddChild(m, text);
            }
        }
    }

    return m;
}  //$end

function GenerateStaff (staffnum, measurenum) {
    //$module(ExportGenerators.mss)
    score = Self._property:ActiveScore;
    this_staff = score.NthStaff(staffnum);
    bar = this_staff[measurenum];

    stf = libmei.Staff();

    if (bar.OnHiddenStave)
    {
        libmei.AddAttribute(stf, 'visible', 'false');
    }

    libmei.AddAttribute(stf, 'n', staffnum);

    layers = GenerateLayers(staffnum, measurenum);
    // NB: Completely resets any previous children!
    libmei.SetChildren(stf, layers);

    return stf;
}  //$end

function GenerateLayers (staffnum, measurenum) {
    //$module(ExportGenerators.mss)
    layerdict = CreateDictionary();
    layers = CreateSparseArray();

    objectPositions = Self._property:ObjectPositions;

    if (objectPositions.PropertyExists(staffnum) = False)
    {
        objectPositions[staffnum] = CreateDictionary();
    }

    staffObjectPositions = objectPositions[staffnum];

    score = Self._property:ActiveScore;
    this_staff = score.NthStaff(staffnum);
    bar = this_staff[measurenum];

    for each bobj in bar
    {
        voicenumber = bobj.VoiceNumber;

        if (staffObjectPositions.PropertyExists(bar.BarNumber) = False)
        {
            staffObjectPositions[bar.BarNumber] = CreateDictionary();
        }

        barObjectPositions = staffObjectPositions[bar.BarNumber];

        if (layerdict.PropertyExists(voicenumber))
        {
            l = layerdict[voicenumber];
        }
        else
        {
            if (voicenumber != 0)
            {
                l = libmei.Layer();
                layers.Push(l._id);

                if (barObjectPositions.PropertyExists(voicenumber) = False)
                {
                    barObjectPositions[voicenumber] = CreateDictionary();
                }

                layerdict[voicenumber] = l;
                libmei.AddAttribute(l, 'n', voicenumber);
            }
        }

        obj = null;
        line = null;
        parent = null;
        beam = null;
        tuplet = null;

        switch (bobj.Type)
        {
            case('Clef')
            {
                clef = GenerateClef(bobj);
                libmei.AddChild(l, clef);
            }
            case('NoteRest')
            {
                note = GenerateNoteRest(bobj, l);

                // record the position of this element
                objVoice = barObjectPositions[voicenumber];
                objVoice[bobj.Position] = note._id;

                if (note._property:TieIds != null)
                {
                    ties = note._property:TieIds;
                    mties = Self._property:MeasureTies;
                    mties = mties.Concat(ties);
                    Self._property:MeasureTies = mties;
                }

                beam = ProcessBeam(bobj, l);
                tuplet = ProcessTuplet(bobj, note, l);

                if (beam != null)
                {
                    libmei.AddChild(beam, note);

                    if (tuplet != null)
                    {
                        if (beam._parent != tuplet._id)
                        {
                            libmei.AddChild(tuplet, beam);
                        }

                        if (tuplet._parent != l._id)
                        {
                            libmei.AddChild(l, tuplet);
                        }
                    }
                    else
                    {
                        if (beam._parent != l._id)
                        {
                            libmei.AddChild(l, beam);
                        }
                    }
                }
                else
                {
                    if (tuplet != null)
                    {
                        libmei.AddChild(tuplet, note);

                        if (tuplet._parent != l._id)
                        {
                            libmei.AddChild(l, tuplet);
                        }
                    }
                    else
                    {
                        libmei.AddChild(l, note);
                    }
                }

            }
            case('BarRest')
            {
                brest = GenerateBarRest(bobj);

                if (brest != null)
                {
                    libmei.AddChild(l, brest);
                }
            }
            case('Slur')
            {
                line = GenerateLine(bobj);
            }
            case('CrescendoLine')
            {
                line = GenerateLine(bobj);
            }
            case('DimuendoLine')
            {
                line = GenerateLine(bobj);
            }
            case('OctavaLine')
            {
                line = GenerateLine(bobj);
            }
            case('Trill')
            {
                line = GenerateLine(bobj);
            }
            case('RepeatTimeLine')
            {
                RegisterVolta(bobj);
            }
            case('Line')
            {
                line = GenerateLine(bobj);
            }
        }

        if (line != null)
        {
            mlines = Self._property:MeasureLines;
            mlines.Push(line._id);
            Self._property:MeasureLines = mlines;
        }
    }

    for each LyricItem lobj in bar
    {
        ProcessLyric(lobj, objectPositions);
    }

    for each SymbolItem sobj in bar
    {
        ProcessSymbol(sobj, objectPositions);
    }

    return layers;
}  //$end

function GenerateClef (bobj) {
    //$module(ExportGenerators.mss)
    clefinfo = ConvertClef(bobj.StyleId);
    clef_el = libmei.Clef();

    libmei.AddAttribute(clef_el, 'shape', clefinfo[0]);
    libmei.AddAttribute(clef_el, 'line', clefinfo[1]);
    libmei.AddAttribute(clef_el, 'dis', clefinfo[2]);
    libmei.AddAttribute(clef_el, 'dis.place', clefinfo[3]);

    return clef_el;
}  //$end

function GenerateNoteRest (bobj, layer) {
    //$module(ExportGenerators.mss)
    nr = null;

    switch(bobj.NoteCount)
    {
        case(0)
        {
            nr = GenerateRest(bobj);
        }
        case(1)
        {
            nobj = bobj[0];
            nr = GenerateNote(nobj);
        }
        default
        {
            nr = GenerateChord(bobj);
        }
    }

    if (bobj.Dx != 0)
    {
        libmei.AddAttribute(nr, 'ho', ConvertOffsets(bobj.Dx));
    }

    if (bobj.CueSize = true and libmei.GetName(nr) != 'space')
    {
        libmei.AddAttribute(nr, 'size', 'cue');
    }

    if (bobj.Hidden = true and libmei.GetName(nr) != 'space')
    {
        libmei.AddAttribute(nr, 'visible', 'false');
    }

    if (bobj.Color != 0)
    {
        nrest_color = ConvertColor(bobj);
        libmei.AddAttribute(nr, 'color', nrest_color);
    }

    if (bobj.GraceNote = true)
    {
        libmei.addAttribute(nr, 'grace', 'acc');
    }

    if (bobj.IsAppoggiatura = true)
    {
        libmei.AddAttribute(nr, 'ornam', 'A');
    }

    if (bobj.IsAcciaccatura = true)
    {
        libmei.AddAttribute(nr, 'ornam', 'a');
    }

    if (bobj.GetArticulation(PauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'curved');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(TriPauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'angular');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(SquarePauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'square');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(StaccatoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacc');
    }

    if (bobj.GetArticulation(DownBowArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'dnbow');
    }

    if (bobj.GetArticulation(UpBowArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'upbow');
    }

    if (bobj.GetArticulation(MarcatoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'marc');
    }

    if (bobj.GetArticulation(AccentArtic))
    {
        libmei.addAttributeValue(nr, 'artic', 'acc');
    }

    if (bobj.GetArticulation(TenutoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'ten');
    }

    if (bobj.GetArticulation(StaccatissimoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacciss');
    }

    if (bobj.GetArticulation(StaccatissimoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacciss');
    }

    if (bobj.GetArticulation(PlusArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stop');
    }

    if (bobj.FallType = FallTypeDoit)
    {
        libmei.AddAttributeValue(nr, 'artic', 'doit');
    }

    if (bobj.FallType = FallTypeNormal)
    {
        libmei.AddAttributeValue(nr, 'artic', 'fall');
    }

    if (bobj.ScoopType = ScoopTypePlop)
    {
        libmei.AddAttributeValue(nr, 'artic', 'plop');
    }

    // a tremolo is a parent of note or chord in MEI
    if ((bobj.SingleTremolos > 0) or (bobj.SingleTremolos = -1))
    {
        // btrem = libmei.BTrem();
        // libmei.AddChild(btrem, nr);

        if (bobj.SingleTremolos = ZOnStem)
        {
            stemmod = 'z';
        }
        else
        {
            stemmod = bobj.SingleTremolos & 'slash';
        }

        libmei.AddAttribute(nr, 'stem.mod', stemmod);
    }

    return nr;
}  //$end

function GenerateRest (bobj) {
    //$module(ExportGenerators.mss)
    // check if it's a rest
    // check if it's hidden, in which case make MEI <space> instead of <rest>
    dur = bobj.Duration;
    meidur = ConvertDuration(dur);

    if (bobj.Hidden)
    {
        r = libmei.Space();
    }
    else
    {
        r = libmei.Rest();
    }

    libmei.AddAttribute(r, 'dur', meidur[0]);
    libmei.AddAttribute(r, 'dur.ges', dur & 'p');
    libmei.AddAttribute(r, 'dots', meidur[1]);

    if (bobj.Dx != 0)
    {
        libmei.AddAttribute(r, 'ho', ConvertOffsets(bobj.Dx));
    }

    if (bobj.Dy != 0)
    {
        libmei.AddAttribute(r, 'vo', ConvertOffsets(bobj.Dy));
    }

    if (bobj.CueSize = true and libmei.GetName(r) != 'space')
    {
        libmei.AddAttribute(r, 'size', 'cue');
    }

    if (bobj.Hidden = true and libmei.GetName(r) != 'space')
    {
        libmei.AddAttribute(r, 'visible', 'false');
    }

    if (bobj.Color != 0)
    {
        nrest_color = ConvertColor(bobj);
        libmei.AddAttribute(r, 'color', nrest_color);
    }

    if (bobj.GetArticulation(PauseArtic))
    {
        libmei.AddAttribute(r, 'fermata', 'above');
    }

    return r;
}  //$end

function GenerateNote (nobj) {
    //$module(ExportGenerators.mss)
    dur = nobj.ParentNoteRest.Duration;
    meidur = ConvertDuration(dur);
    ntinfo = ConvertDiatonicPitch(nobj.DiatonicPitch);
    pos = nobj.ParentNoteRest.Position;
    keysig = nobj.ParentNoteRest.ParentBar.GetKeySignatureAt(pos);

    n = libmei.Note();
    hash = SimpleNoteHash(nobj);
    n._property:hash = hash;

    libmei.AddAttribute(n, 'pnum', nobj.Pitch);
    libmei.AddAttribute(n, 'pname', ntinfo[0]);
    libmei.AddAttribute(n, 'oct', ntinfo[1]);
    libmei.AddAttribute(n, 'dur', meidur[0]);
    libmei.AddAttribute(n, 'dur.ges', dur & 'p');
    libmei.AddAttribute(n, 'dots', meidur[1]);

    // Accidentals will always be encoded as child elements, not attributes
    staff = nobj.ParentNoteRest.ParentBar.ParentStaff.StaffNum;

    if (nobj.NoteStyle != NormalNoteStyle)
    {
        nstyle = ConvertNoteStyle(nobj.NoteStyle);
        libmei.AddAttribute(n, 'headshape', nstyle);
    }

    accid = ConvertAccidental(nobj, keysig.Sharps);
    accVal = accid[0];
    isVisible = accid[1];

    if (accVal != ' ')
    {
        child = libmei.Accid();
        libmei.AddChild(n, child);

        if (isVisible = True)
        {
            libmei.AddAttribute(child, 'accid', accVal);
        }
        else
        {
            libmei.AddAttribute(child, 'accid.ges', accVal);
        }

        switch (nobj.AccidentalStyle)
        {
            case (CautionaryAcc)
            {
                libmei.AddAttribute(child, 'func', 'caution');
            }
            case (BracketedAcc)
            {
                libmei.AddAttribute(child, 'enclose', 'paren');
            }
        }
    }

    /*
        Ties
    */
    tieresolver = Self._property:TieResolver;
    if (tieresolver.PropertyExists(hash))
    {
        // this note is the end of the tie.
        tie_id = tieresolver[hash];
        tie = libmei.getElementById(tie_id);
        libmei.AddAttribute(tie, 'endid', '#' & n._id);
    }

    if (nobj.Tied = True)
    {
        // set the end hash
        parent_nr = nobj.ParentNoteRest;
        next_object = parent_nr.NextItem(parent_nr.VoiceNumber, 'NoteRest');

        if (next_object = null)
        {
            // check the first object in the next bar
            next_bar_num = parent_nr.ParentBar.BarNumber + 1;
            next_bar = parent_nr.ParentBar.ParentStaff.NthBar(next_bar_num);
            next_object = next_bar.NthBarObject(0);
        }

        for each next_note in next_object
        {
            if (nobj.Pitch = next_note.Pitch)
            {
                // it's probably this one.
                endhash = SimpleNoteHash(next_note);
                tie = libmei.Tie();
                libmei.AddAttribute(tie, 'startid', '#' & n._id);
                tieresolver[endhash] = tie._id;

                n._property:TieIds = CreateSparseArray(tie._id);
            }
        }
    }

    return n;
}  //$end

function GenerateChord (bobj) {
    //$module(ExportGenerators.mss)
    n = libmei.Chord();
    dur = bobj.Duration;
    meidur = ConvertDuration(dur);

    libmei.AddAttribute(n, 'dur', meidur[0]);
    libmei.AddAttribute(n, 'dur.ges', dur & 'p');
    libmei.AddAttribute(n, 'dots', meidur[1]);

    tiearr = CreateSparseArray();

    for each note in bobj
    {
        sn = GenerateNote(note);
        libmei.AddChild(n, sn);

        if (sn._property:TieIds != null)
        {
            tieids = sn._property:TieIds;
            tiearr = tiearr.Concat(tieids);
        }
    }

    n._property:TieIds = tiearr;

    return n;
}  //$end

function GenerateBarRest (bobj) {
    //$module(ExportGenerators.mss)
    switch (bobj.RestType)
    {
        case(BreveBarRest)
        {
            obj = libmei.MRest();
            libmei.AddAttribute(obj, 'dur', 'breve');
        }
        case (WholeBarRest)
        {
            obj = libmei.MRest();
        }
        case (OneBarRepeat)
        {
            obj = libmei.MRpt();
        }
        case (TwoBarRepeat)
        {
            obj = libmei.MRpt2();
        }
        case (FourBarRepeat)
        {
            warnings = Self._property:warnings;
            warnings.Push(utils.Format(_ObjectHasNoMEISupport, 'A four-bar repeat'));
            return null;
        }
    }

    if (bobj.Hidden = true)
    {
        libmei.AddAttribute(obj, 'visible', 'false');
    }

    return obj;
}  //$end

function GenerateScoreDef (score) {
    //$module(ExportGenerators.mss)
    scoredef = libmei.ScoreDef();
    Self._property:_GlobalScoreDef = libmei.GetId(scoredef);

    docSettings = score.DocumentSetup;

    // this will ensure that the units specified by the user is the one that is
    // represented in the output.
    sibUnits = docSettings.UnitsInDocumentSetupDialog;
    unit = '';

    switch(sibUnits)
    {
        case(DocumentSetupUnitsInches)
        {
            unit = 'in';
        }
        case(DocumentSetupUnitsPoints)
        {
            unit = 'pt';
        }
        default
        {
            // Millimeters is the default for Sibelius
            unit = 'mm';
        }
    }

    libmei.AddAttribute(scoredef, 'page.width', docSettings.PageWidth & unit);
    libmei.AddAttribute(scoredef, 'page.height', docSettings.PageHeight & unit);
    libmei.AddAttribute(scoredef, 'page.leftmar', docSettings.PageLeftMargin & unit);
    libmei.AddAttribute(scoredef, 'page.rightmar', docSettings.PageRightMargin & unit);
    libmei.AddAttribute(scoredef, 'page.topmar', docSettings.PageTopMargin & unit);
    libmei.AddAttribute(scoredef, 'page.botmar', docSettings.PageBottomMargin & unit);

    showCautionaryAccidentals = score.EngravingRules.CautionaryNaturalsInKeySignatures;
    if (showCautionaryAccidentals = true)
    {
        libmei.AddAttribute(scoredef, 'key.sig.showchange', 'true');
    }

    systf = score.SystemStaff;
    timesig = systf.CurrentTimeSignature(1);

    libmei.AddAttribute(scoredef, 'meter.count', timesig.Numerator);
    libmei.AddAttribute(scoredef, 'meter.unit', timesig.Denominator);
    libmei.AddAttribute(scoredef, 'meter.sym', ConvertNamedTimeSignature(timesig.Text));
    libmei.AddAttribute(scoredef, 'ppq', '256'); // sibelius' internal ppq.

    staffgrp = GenerateStaffGroups(score);
    libmei.AddChild(scoredef, staffgrp);

    return scoredef;
}  //$end

function GenerateStaffGroups (score) {
    //$module(ExportGenerators.mss)
    staffdict = CreateDictionary();
    parentstgrp = libmei.StaffGrp();
    numstaff = score.StaffCount;

    for each Staff s in score
    {
        std = libmei.StaffDef();
        libmei.XMLIdToObjectMap[std._id] = s;

        libmei.AddAttribute(std, 'n', s.StaffNum);
        libmei.AddAttribute(std, 'label', s.FullInstrumentName);
        libmei.AddAttribute(std, 'lines', s.InitialInstrumentType.NumStaveLines);

        clefinfo = ConvertClef(s.InitialClefStyleId);
        libmei.AddAttribute(std, 'clef.shape', clefinfo[0]);
        libmei.AddAttribute(std, 'clef.line', clefinfo[1]);
        libmei.AddAttribute(std, 'clef.dis', clefinfo[2]);
        libmei.AddAttribute(std, 'clef.dis.place', clefinfo[3]);
        libmei.AddAttribute(std, 'key.sig', ConvertKeySignature(s.InitialKeySignature.Sharps));

        if (s.InitialKeySignature.Major)
        {
            libmei.AddAttribute(std, 'key.mode', 'major');
        }
        else
        {
            libmei.AddAttribute(std, 'key.mode', 'minor');
        }

        staffdict[s.StaffNum] = std;
    }

    stgpdict = CreateDictionary();
    stgprevidx = CreateDictionary();
    stgpnum = 1;
    brackets = score.BracketsAndBraces;

    for each bkt in brackets
    {
        stgp = libmei.StaffGrp();
        libmei.AddAttribute(stgp, 'symbol', ConvertBracket(bkt.BracketType));
        libmei.AddAttribute(stgp, 'n', stgpnum);
        stgpdict[stgpnum] = stgp;

        if (bkt.BracketType != BracketSub)
        {
            for i = bkt.TopStaveNum to bkt.BottomStaveNum + 1
            {
                libmei.AddChild(stgp, staffdict[i]);
                stgprevidx[i] = stgpnum;
            }
        }
        stgpnum = stgpnum + 1;
    }

    // finally, we need to put them all in the right order.
    stgpnum_added = CreateSparseArray();
    for j = 1 to numstaff + 1
    {
        if (stgprevidx.PropertyExists(j))
        {
            // there is a group
            stgpnum = stgprevidx[j];
            if (utils.IsInArray(stgpnum_added, stgpnum) = false)
            {
                libmei.AddChild(parentstgrp, stgpdict[stgpnum]);
                stgpnum_added.Push(stgpnum);
            }
        }
        else
        {
            libmei.AddChild(parentstgrp, staffdict[j]);
        }

    }
    return parentstgrp;
}  //$end

function GenerateLine (bobj) {
    //$module(ExportGenerators.mss)
    line = null;
    bar = bobj.ParentBar;

    switch (bobj.Type)
    {
        case ('Slur')
        {
            line = libmei.Slur();
            slurrend = ConvertSlurStyle(bobj.StyleId);
            libmei.AddAttribute(line, 'rend', slurrend[1]);
        }
        case ('CrescendoLine')
        {
            line = libmei.Hairpin();
            libmei.AddAttribute(line, 'form', 'cres');
        }
        case ('DimuendoLine')
        {
            line = libmei.Hairpin();
            libmei.AddAttribute(line, 'form', 'dim');
        }
        case ('OctavaLine')
        {
            line = libmei.Octave();
            octrend = ConvertOctava(bobj.StyleId);
            libmei.AddAttribute(octl, 'dis', octrend[0]);
            libmei.AddAttribute(octl, 'dis.place', octrend[1]);
        }
        case ('Trill')
        {
            line = GenerateTrill(bobj);
            // NB: Return here since the trill already has its properties set.
            return line;
        }
        case ('Line')
        {
            // a generic line element.
            linecomps = MSplitString(bobj.StyleId, '.');
            switch(linecomps[2])
            {
                case('bracket')
                {
                    line = libmei.Line();
                    libmei.AddAttribute(line, 'type', 'bracket');
                    if (linecomps.Length > 4)
                    {
                        if (linecomps[4] = 'start')
                        {
                            libmei.AddAttribute(line, 'subtype', 'start');
                        }

                        if (linecomps[4] = 'end')
                        {
                            libmei.AddAttribute(line, 'subtype', 'end');
                        }
                    }
                }
            }
        }
    }

    if (line = null)
    {
        return null;
    }

    line = AddBarObjectInfoToElement(bobj, line);

    return line;
}  //$end

function GenerateTrill (bobj) {
    //$module(ExportGenerators.mss)
    /* There are two types of trills in Sibelius: A line object and a
        symbol object. This method normalizes both of these.
    */
    trill = libmei.Trill();
    voicenum = bobj.VoiceNumber;
    bar = bobj.ParentBar;

    if (voicenum = 0)
    {
        warnings = Self._property:warnings;
        warnings.Push(utils.Format(_ObjectAssignedToAllVoicesWarning, bobj.Type));
        voicenum = 1;
    }

    obj = GetNoteObjectAtPosition(bobj);

    if (obj != null)
    {
        libmei.AddAttribute(trill, 'startid', '#' & obj._id);
    }

    trill = AddBarObjectInfoToElement(bobj, trill);

    return trill;
}  //$end

function GenerateMEIHeader () {
    //$module(ExportGenerators.mss)
    // takes in a Sibelius Score object
    // returns a libmei tree (i.e., nested objects and arrays) with a MEI header with metadata
    score = Self._property:ActiveScore;
    header = libmei.MeiHead();
    fileD = libmei.FileDesc();
    titleS = libmei.TitleStmt();
    libmei.AddChild(header, fileD);
    libmei.AddChild(fileD, titleS);

    workDesc = libmei.WorkDesc();
    wd_work = libmei.Work();
    wd_titleStmt = libmei.TitleStmt();
    wd_title = libmei.Title();
    wd_respStmt = libmei.RespStmt();
    libmei.AddChild(wd_titleStmt, wd_title);
    libmei.AddChild(wd_titleStmt, wd_respStmt);
    libmei.AddChild(wd_work, wd_titleStmt);
    libmei.AddChild(workDesc, wd_work);

    title = libmei.Title();
    libmei.AddChild(titleS, title);

    if (score.Title != '')
    {
        libmei.SetText(title, score.Title);
        libmei.SetText(wd_title, score.Title);
    }
    if (score.Subtitle != '')
    {
        subtitle = libmei.Title();
        libmei.AddChild(titleS, subtitle);
        libmei.SetText(subtitle, score.Subtitle);
        libmei.AddAttribute(subtitle, 'type', 'subtitle');
    }
    if (score.Composer != '')
    {
        composer = libmei.Composer();
        libmei.AddChild(titleS, composer);
        libmei.SetText(composer, score.Composer);
        wd_composer = libmei.PersName();
        libmei.AddAttribute(wd_composer, 'role', 'composer');
        libmei.SetText(wd_composer, score.Composer);
        libmei.AddChild(wd_respStmt, wd_composer);
    }
    if (score.Lyricist != '')
    {
        lyricist = libmei.Lyricist();
        libmei.AddChild(titleS, lyricist);
        libmei.SetText(lyricist, score.Lyricist);
        wd_lyricist = libmei.PersName();
        libmei.AddAttribute(wd_lyricist, 'role', 'lyricist');
        libmei.SetText(wd_lyricist, score.Lyricist);
        libmei.AddChild(wd_respStmt, wd_lyricist);
    }
    if (score.Arranger != '')
    {
        arranger = libmei.Arranger();
        libmei.AddChild(titleS, arranger);
        libmei.SetText(arranger, score.Arranger);
        wd_arranger = libmei.PersName();
        libmei.AddAttribute(wd_arranger, 'role', 'arranger');
        libmei.SetText(wd_arranger, score.Arranger);
        libmei.AddChild(wd_respStmt, wd_arranger);
    }
    if (score.OtherInformation != '')
    {
        wd_notesStmt = libmei.NotesStmt();
        ns_annot = libmei.Annot();
        libmei.AddChild(wd_work, wd_notesStmt);
        libmei.AddChild(wd_notesStmt, ns_annot);
        libmei.SetText(ns_annot, score.OtherInformation);
    }

    respS = libmei.RespStmt();
    libmei.AddChild(titleS, respS);
    persN = libmei.PersName();
    libmei.AddChild(respS, persN);
    pubS = libmei.PubStmt();
    libmei.AddChild(fileD, pubS);

    avail = libmei.Availability();
    ur = libmei.UseRestrict();
    libmei.AddChild(avail, ur);
    libmei.AddChild(pubS, avail);
    libmei.SetText(ur, score.Copyright);

    encodingD = libmei.EncodingDesc();
    libmei.AddChild(header, encodingD);
    appI = libmei.AppInfo();
    libmei.AddChild(encodingD, appI);

    applic = libmei.Application();
    libmei.SetId(applic, 'sibelius7');
    libmei.AddChild(appI, applic);
    libmei.AddAttribute(applic, 'version', Sibelius.ProgramVersion);
    nm = libmei.Name();
    libmei.SetText(nm, 'Sibelius (' & Version & ')');
    libmei.AddChild(applic, nm);

    libmei.AddChild(header, workDesc);

    return header;
}  //$end

function GenerateMEIMusic () {
    //$module(ExportGenerators.mss)
    score = Self._property:ActiveScore;

    Self._property:TieResolver = CreateDictionary();
    Self._property:LyricWords = CreateDictionary();
    Self._property:SpecialBarlines = CreateDictionary();
    Self._property:SystemText = CreateDictionary();
    Self._property:LayerObjectPositions = null;
    Self._property:ObjectPositions = CreateDictionary();

    // the section parent is used in case we need to inject
    // a new parent later (e.g., for endings)
    Self._property:SectionParent = null;
    Self._property:VoltaBars = CreateDictionary();
    Self._property:ActiveVolta = null;
    Self._property:VoltaElement = null;

    // grab some global markers from the system staff
    // This will store it for later use.
    ProcessSystemStaff(score);

    music = libmei.Music();
    body = libmei.Body();
    mdiv = libmei.Mdiv();
    sco = libmei.Score();

    libmei.AddChild(music, body);
    libmei.AddChild(body, mdiv);
    libmei.AddChild(mdiv, sco);

    scd = sibmei2.GenerateScoreDef(score);
    libmei.AddChild(sco, scd);

    barmap = ConvertSibeliusStructure(score);
    numbars = barmap.GetPropertyNames();
    numbars = numbars.Length;
    Self._property:BarMap = barmap;

    section = libmei.Section();
    libmei.AddChild(sco, section);

    for j = 1 to numbars + 1
    {
        // this value may get changed by the volta processor
        // to inject a new parent in the hierarchy.
        progressMsg = utils.Format(_ExportingBars, j, numbars);
        cont = Sibelius.UpdateProgressDialog(j, progressMsg);

        if (cont = 0)
        {
            ExitPlugin();
        }

        m = GenerateMeasure(j);
        ending = ProcessVolta(j);

        if (ending != null)
        {
            libmei.AddChild(section, ending);
            libmei.AddChild(ending, m);
        }
        else
        {
            libmei.AddChild(section, m);
        }
    }

    return music;
}  //$end

function GenerateMeasure (num) {
    //$module(ExportGenerators.mss)

    score = Self._property:ActiveScore;
    // measureties
    Self._property:MeasureTies = CreateSparseArray();
    Self._property:MeasureLines = CreateSparseArray();

    m = libmei.Measure();
    libmei.AddAttribute(m, 'n', num);

    barmap = Self._property:BarMap;
    staves = barmap[num];
    children = CreateSparseArray();

    // since so much metadata about the staff and other context
    // is available on the bar that should now be on the measure, go through the bars
    // and try to extract it.

    for i = 1 to staves.Length + 1
    {
        this_staff = score.NthStaff(i);
        bar = this_staff[num];

        if (bar.ExternalBarNumberString)
        {
            libmei.AddAttribute(m, 'label', bar.ExternalBarNumberString);
        }

        s = GenerateStaff(i, num);
        libmei.AddChild(m, s);
    }

    mties = Self._property:MeasureTies;

    for each tie in mties
    {
        m.children.Push(tie);
    }

    mlines = Self._property:MeasureLines;

    for each line in mlines
    {
        m.children.Push(line);
    }

    specialbarlines = Self._property:SpecialBarlines;

    if (specialbarlines.PropertyExists(num))
    {
        bline = specialbarlines[num];

        if (bline = 'rptstart')
        {
            libmei.AddAttribute(m, 'left', bline);
        }
        else
        {
            libmei.AddAttribute(m, 'right', bline);
        }
    }

    systemtext = Self._property:SystemText;

    if (systemtext.PropertyExists(num))
    {
        textobjs = systemtext[num];
        for each textobj in textobjs
        {
            text = ConvertText(textobj);

            if (text != null)
            {
                libmei.AddChild(m, text);
            }
        }
    }

    return m;
}  //$end

function GenerateStaff (staffnum, measurenum) {
    //$module(ExportGenerators.mss)
    score = Self._property:ActiveScore;
    this_staff = score.NthStaff(staffnum);
    bar = this_staff[measurenum];

    stf = libmei.Staff();

    if (bar.OnHiddenStave)
    {
        libmei.AddAttribute(stf, 'visible', 'false');
    }

    libmei.AddAttribute(stf, 'n', staffnum);

    layers = GenerateLayers(staffnum, measurenum);
    // NB: Completely resets any previous children!
    libmei.SetChildren(stf, layers);

    return stf;
}  //$end

function GenerateLayers (staffnum, measurenum) {
    //$module(ExportGenerators.mss)
    layerdict = CreateDictionary();
    layers = CreateSparseArray();

    objectPositions = Self._property:ObjectPositions;

    if (objectPositions.PropertyExists(staffnum) = False)
    {
        objectPositions[staffnum] = CreateDictionary();
    }

    staffObjectPositions = objectPositions[staffnum];

    score = Self._property:ActiveScore;
    this_staff = score.NthStaff(staffnum);
    bar = this_staff[measurenum];

    for each bobj in bar
    {
        voicenumber = bobj.VoiceNumber;

        if (staffObjectPositions.PropertyExists(bar.BarNumber) = False)
        {
            staffObjectPositions[bar.BarNumber] = CreateDictionary();
        }

        barObjectPositions = staffObjectPositions[bar.BarNumber];

        if (layerdict.PropertyExists(voicenumber))
        {
            l = layerdict[voicenumber];
        }
        else
        {
            if (voicenumber != 0)
            {
                l = libmei.Layer();
                layers.Push(l._id);

                if (barObjectPositions.PropertyExists(voicenumber) = False)
                {
                    barObjectPositions[voicenumber] = CreateDictionary();
                }

                layerdict[voicenumber] = l;
                libmei.AddAttribute(l, 'n', voicenumber);
            }
        }

        obj = null;
        line = null;
        parent = null;
        beam = null;
        tuplet = null;

        switch (bobj.Type)
        {
            case('Clef')
            {
                clef = GenerateClef(bobj);
                libmei.AddChild(l, clef);
            }
            case('NoteRest')
            {
                note = GenerateNoteRest(bobj, l);

                // record the position of this element
                objVoice = barObjectPositions[voicenumber];
                objVoice[bobj.Position] = note._id;

                if (note._property:TieIds != null)
                {
                    ties = note._property:TieIds;
                    mties = Self._property:MeasureTies;
                    mties = mties.Concat(ties);
                    Self._property:MeasureTies = mties;
                }

                beam = ProcessBeam(bobj, l);
                tuplet = ProcessTuplet(bobj, note, l);

                if (beam != null)
                {
                    libmei.AddChild(beam, note);

                    if (tuplet != null)
                    {
                        if (beam._parent != tuplet._id)
                        {
                            libmei.AddChild(tuplet, beam);
                        }

                        if (tuplet._parent != l._id)
                        {
                            libmei.AddChild(l, tuplet);
                        }
                    }
                    else
                    {
                        if (beam._parent != l._id)
                        {
                            libmei.AddChild(l, beam);
                        }
                    }
                }
                else
                {
                    if (tuplet != null)
                    {
                        libmei.AddChild(tuplet, note);

                        if (tuplet._parent != l._id)
                        {
                            libmei.AddChild(l, tuplet);
                        }
                    }
                    else
                    {
                        libmei.AddChild(l, note);
                    }
                }

            }
            case('BarRest')
            {
                brest = GenerateBarRest(bobj);

                if (brest != null)
                {
                    libmei.AddChild(l, brest);
                }
            }
            case('Slur')
            {
                line = GenerateLine(bobj);
            }
            case('CrescendoLine')
            {
                line = GenerateLine(bobj);
            }
            case('DimuendoLine')
            {
                line = GenerateLine(bobj);
            }
            case('OctavaLine')
            {
                line = GenerateLine(bobj);
            }
            case('Trill')
            {
                line = GenerateLine(bobj);
            }
            case('RepeatTimeLine')
            {
                RegisterVolta(bobj);
            }
            case('Line')
            {
                line = GenerateLine(bobj);
            }
        }

        if (line != null)
        {
            mlines = Self._property:MeasureLines;
            mlines.Push(line._id);
            Self._property:MeasureLines = mlines;
        }
    }

    for each LyricItem lobj in bar
    {
        ProcessLyric(lobj, objectPositions);
    }

    for each SymbolItem sobj in bar
    {
        ProcessSymbol(sobj, objectPositions);
    }

    return layers;
}  //$end

function GenerateClef (bobj) {
    //$module(ExportGenerators.mss)
    clefinfo = ConvertClef(bobj.StyleId);
    clef_el = libmei.Clef();

    libmei.AddAttribute(clef_el, 'shape', clefinfo[0]);
    libmei.AddAttribute(clef_el, 'line', clefinfo[1]);
    libmei.AddAttribute(clef_el, 'dis', clefinfo[2]);
    libmei.AddAttribute(clef_el, 'dis.place', clefinfo[3]);

    return clef_el;
}  //$end

function GenerateNoteRest (bobj, layer) {
    //$module(ExportGenerators.mss)
    nr = null;

    switch(bobj.NoteCount)
    {
        case(0)
        {
            nr = GenerateRest(bobj);
        }
        case(1)
        {
            nobj = bobj[0];
            nr = GenerateNote(nobj);
        }
        default
        {
            nr = GenerateChord(bobj);
        }
    }

    if (bobj.Dx != 0)
    {
        libmei.AddAttribute(nr, 'ho', ConvertOffsets(bobj.Dx));
    }

    if (bobj.CueSize = true and libmei.GetName(nr) != 'space')
    {
        libmei.AddAttribute(nr, 'size', 'cue');
    }

    if (bobj.Hidden = true and libmei.GetName(nr) != 'space')
    {
        libmei.AddAttribute(nr, 'visible', 'false');
    }

    if (bobj.Color != 0)
    {
        nrest_color = ConvertColor(bobj);
        libmei.AddAttribute(nr, 'color', nrest_color);
    }

    if (bobj.GraceNote = true)
    {
        libmei.addAttribute(nr, 'grace', 'acc');
    }

    if (bobj.IsAppoggiatura = true)
    {
        libmei.AddAttribute(nr, 'ornam', 'A');
    }

    if (bobj.IsAcciaccatura = true)
    {
        libmei.AddAttribute(nr, 'ornam', 'a');
    }

    if (bobj.GetArticulation(PauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'curved');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(TriPauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'angular');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(SquarePauseArtic))
    {
        fermata = libmei.Fermata();
        libmei.AddAttribute(fermata, 'form', 'norm');
        libmei.AddAttribute(fermata, 'shape', 'square');
        libmei.AddAttribute(fermata, 'startid', '#' & nr._id);
        libmei.AddAttribute(fermata, 'layer', bobj.VoiceNumber);
        libmei.AddAttribute(fermata, 'staff', bobj.ParentBar.ParentStaff.StaffNum);

        mlines = Self._property:MeasureLines;
        mlines.Push(fermata._id);
    }

    if (bobj.GetArticulation(StaccatoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacc');
    }

    if (bobj.GetArticulation(DownBowArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'dnbow');
    }

    if (bobj.GetArticulation(UpBowArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'upbow');
    }

    if (bobj.GetArticulation(MarcatoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'marc');
    }

    if (bobj.GetArticulation(AccentArtic))
    {
        libmei.addAttributeValue(nr, 'artic', 'acc');
    }

    if (bobj.GetArticulation(TenutoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'ten');
    }

    if (bobj.GetArticulation(StaccatissimoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacciss');
    }

    if (bobj.GetArticulation(StaccatissimoArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stacciss');
    }

    if (bobj.GetArticulation(PlusArtic))
    {
        libmei.AddAttributeValue(nr, 'artic', 'stop');
    }

    if (bobj.FallType = FallTypeDoit)
    {
        libmei.AddAttributeValue(nr, 'artic', 'doit');
    }

    if (bobj.FallType = FallTypeNormal)
    {
        libmei.AddAttributeValue(nr, 'artic', 'fall');
    }

    if (bobj.ScoopType = ScoopTypePlop)
    {
        libmei.AddAttributeValue(nr, 'artic', 'plop');
    }

    // a tremolo is a parent of note or chord in MEI
    if ((bobj.SingleTremolos > 0) or (bobj.SingleTremolos = -1))
    {
        // btrem = libmei.BTrem();
        // libmei.AddChild(btrem, nr);

        if (bobj.SingleTremolos = ZOnStem)
        {
            stemmod = 'z';
        }
        else
        {
            stemmod = bobj.SingleTremolos & 'slash';
        }

        libmei.AddAttribute(nr, 'stem.mod', stemmod);
    }

    return nr;
}  //$end

function GenerateRest (bobj) {
    //$module(ExportGenerators.mss)
    // check if it's a rest
    // check if it's hidden, in which case make MEI <space> instead of <rest>
    dur = bobj.Duration;
    meidur = ConvertDuration(dur);

    if (bobj.Hidden)
    {
        r = libmei.Space();
    }
    else
    {
        r = libmei.Rest();
    }

    libmei.AddAttribute(r, 'dur', meidur[0]);
    libmei.AddAttribute(r, 'dur.ges', dur & 'p');
    libmei.AddAttribute(r, 'dots', meidur[1]);

    if (bobj.Dx != 0)
    {
        libmei.AddAttribute(r, 'ho', ConvertOffsets(bobj.Dx));
    }

    if (bobj.Dy != 0)
    {
        libmei.AddAttribute(r, 'vo', ConvertOffsets(bobj.Dy));
    }

    if (bobj.CueSize = true and libmei.GetName(r) != 'space')
    {
        libmei.AddAttribute(r, 'size', 'cue');
    }

    if (bobj.Hidden = true and libmei.GetName(r) != 'space')
    {
        libmei.AddAttribute(r, 'visible', 'false');
    }

    if (bobj.Color != 0)
    {
        nrest_color = ConvertColor(bobj);
        libmei.AddAttribute(r, 'color', nrest_color);
    }

    if (bobj.GetArticulation(PauseArtic))
    {
        libmei.AddAttribute(r, 'fermata', 'above');
    }

    return r;
}  //$end

function GenerateNote (nobj) {
    //$module(ExportGenerators.mss)
    dur = nobj.ParentNoteRest.Duration;
    meidur = ConvertDuration(dur);
    ntinfo = ConvertDiatonicPitch(nobj.DiatonicPitch);
    pos = nobj.ParentNoteRest.Position;
    keysig = nobj.ParentNoteRest.ParentBar.GetKeySignatureAt(pos);

    n = libmei.Note();
    hash = SimpleNoteHash(nobj);
    n._property:hash = hash;

    libmei.AddAttribute(n, 'pnum', nobj.Pitch);
    libmei.AddAttribute(n, 'pname', ntinfo[0]);
    libmei.AddAttribute(n, 'oct', ntinfo[1]);
    libmei.AddAttribute(n, 'dur', meidur[0]);
    libmei.AddAttribute(n, 'dur.ges', dur & 'p');
    libmei.AddAttribute(n, 'dots', meidur[1]);

    // Accidentals will always be encoded as child elements, not attributes
    staff = nobj.ParentNoteRest.ParentBar.ParentStaff.StaffNum;

    if (nobj.NoteStyle != NormalNoteStyle)
    {
        nstyle = ConvertNoteStyle(nobj.NoteStyle);
        libmei.AddAttribute(n, 'headshape', nstyle);
    }

    accid = ConvertAccidental(nobj, keysig.Sharps);
    accVal = accid[0];
    isVisible = accid[1];

    if (accVal != ' ')
    {
        child = libmei.Accid();
        libmei.AddChild(n, child);

        if (isVisible = True)
        {
            libmei.AddAttribute(child, 'accid', accVal);
        }
        else
        {
            libmei.AddAttribute(child, 'accid.ges', accVal);
        }

        switch (nobj.AccidentalStyle)
        {
            case (CautionaryAcc)
            {
                libmei.AddAttribute(child, 'func', 'caution');
            }
            case (BracketedAcc)
            {
                libmei.AddAttribute(child, 'enclose', 'paren');
            }
        }
    }

    /*
        Ties
    */
    tieresolver = Self._property:TieResolver;
    if (tieresolver.PropertyExists(hash))
    {
        // this note is the end of the tie.
        tie_id = tieresolver[hash];
        tie = libmei.getElementById(tie_id);
        libmei.AddAttribute(tie, 'endid', '#' & n._id);
    }

    if (nobj.Tied = True)
    {
        // set the end hash
        parent_nr = nobj.ParentNoteRest;
        next_object = parent_nr.NextItem(parent_nr.VoiceNumber, 'NoteRest');

        if (next_object = null)
        {
            // check the first object in the next bar
            next_bar_num = parent_nr.ParentBar.BarNumber + 1;
            next_bar = parent_nr.ParentBar.ParentStaff.NthBar(next_bar_num);
            next_object = next_bar.NthBarObject(0);
        }

        for each next_note in next_object
        {
            if (nobj.Pitch = next_note.Pitch)
            {
                // it's probably this one.
                endhash = SimpleNoteHash(next_note);
                tie = libmei.Tie();
                libmei.AddAttribute(tie, 'startid', '#' & n._id);
                tieresolver[endhash] = tie._id;

                n._property:TieIds = CreateSparseArray(tie._id);
            }
        }
    }

    return n;
}  //$end

function GenerateChord (bobj) {
    //$module(ExportGenerators.mss)
    n = libmei.Chord();
    dur = bobj.Duration;
    meidur = ConvertDuration(dur);

    libmei.AddAttribute(n, 'dur', meidur[0]);
    libmei.AddAttribute(n, 'dur.ges', dur & 'p');
    libmei.AddAttribute(n, 'dots', meidur[1]);

    tiearr = CreateSparseArray();

    for each note in bobj
    {
        sn = GenerateNote(note);
        libmei.AddChild(n, sn);

        if (sn._property:TieIds != null)
        {
            tieids = sn._property:TieIds;
            tiearr = tiearr.Concat(tieids);
        }
    }

    n._property:TieIds = tiearr;

    return n;
}  //$end

function GenerateBarRest (bobj) {
    //$module(ExportGenerators.mss)
    switch (bobj.RestType)
    {
        case (WholeBarRest)
        {
            obj = libmei.MRest();
        }
        case (OneBarRepeat)
        {
            obj = libmei.MRpt();
        }
        case (TwoBarRepeat)
        {
            obj = libmei.MRpt2();
        }
        case (FourBarRepeat)
        {
            warnings = Self._property:warnings;
            warnings.Push(utils.Format(_ObjectHasNoMEISupport, 'A four-bar repeat'));
            return null;
        }
    }

    if (bobj.Hidden = true)
    {
        libmei.AddAttribute(obj, 'visible', 'false');
    }

    return obj;
}  //$end

function GenerateScoreDef (score) {
    //$module(ExportGenerators.mss)
    scoredef = libmei.ScoreDef();
    Self._property:_GlobalScoreDef = libmei.GetId(scoredef);

    docSettings = score.DocumentSetup;

    // this will ensure that the units specified by the user is the one that is
    // represented in the output.
    sibUnits = docSettings.UnitsInDocumentSetupDialog;
    unit = '';

    switch(sibUnits)
    {
        case(DocumentSetupUnitsInches)
        {
            unit = 'in';
        }
        case(DocumentSetupUnitsPoints)
        {
            unit = 'pt';
        }
        default
        {
            // Millimeters is the default for Sibelius
            unit = 'mm';
        }
    }

    libmei.AddAttribute(scoredef, 'page.width', docSettings.PageWidth & unit);
    libmei.AddAttribute(scoredef, 'page.height', docSettings.PageHeight & unit);
    libmei.AddAttribute(scoredef, 'page.leftmar', docSettings.PageLeftMargin & unit);
    libmei.AddAttribute(scoredef, 'page.rightmar', docSettings.PageRightMargin & unit);
    libmei.AddAttribute(scoredef, 'page.topmar', docSettings.PageTopMargin & unit);
    libmei.AddAttribute(scoredef, 'page.botmar', docSettings.PageBottomMargin & unit);

    showCautionaryAccidentals = score.EngravingRules.CautionaryNaturalsInKeySignatures;
    if (showCautionaryAccidentals = true)
    {
        libmei.AddAttribute(scoredef, 'key.sig.showchange', 'true');
    }

    systf = score.SystemStaff;
    timesig = systf.CurrentTimeSignature(1);

    libmei.AddAttribute(scoredef, 'meter.count', timesig.Numerator);
    libmei.AddAttribute(scoredef, 'meter.unit', timesig.Denominator);
    libmei.AddAttribute(scoredef, 'meter.sym', ConvertNamedTimeSignature(timesig.Text));
    libmei.AddAttribute(scoredef, 'ppq', '256'); // sibelius' internal ppq.

    staffgrp = GenerateStaffGroups(score);
    libmei.AddChild(scoredef, staffgrp);

    return scoredef;
}  //$end

function GenerateStaffGroups (score) {
    //$module(ExportGenerators.mss)
    staffdict = CreateDictionary();
    parentstgrp = libmei.StaffGrp();
    numstaff = score.StaffCount;

    for each Staff s in score
    {
        std = libmei.StaffDef();
        libmei.XMLIdToObjectMap[std._id] = s;

        libmei.AddAttribute(std, 'n', s.StaffNum);
        libmei.AddAttribute(std, 'label', s.FullInstrumentName);
        libmei.AddAttribute(std, 'lines', s.InitialInstrumentType.NumStaveLines);

        clefinfo = ConvertClef(s.InitialClefStyleId);
        libmei.AddAttribute(std, 'clef.shape', clefinfo[0]);
        libmei.AddAttribute(std, 'clef.line', clefinfo[1]);
        libmei.AddAttribute(std, 'clef.dis', clefinfo[2]);
        libmei.AddAttribute(std, 'clef.dis.place', clefinfo[3]);
        libmei.AddAttribute(std, 'key.sig', ConvertKeySignature(s.InitialKeySignature.Sharps));

        if (s.InitialKeySignature.Major)
        {
            libmei.AddAttribute(std, 'key.mode', 'major');
        }
        else
        {
            libmei.AddAttribute(std, 'key.mode', 'minor');
        }

        staffdict[s.StaffNum] = std;
    }

    stgpdict = CreateDictionary();
    stgprevidx = CreateDictionary();
    stgpnum = 1;
    brackets = score.BracketsAndBraces;

    for each bkt in brackets
    {
        stgp = libmei.StaffGrp();
        libmei.AddAttribute(stgp, 'symbol', ConvertBracket(bkt.BracketType));
        libmei.AddAttribute(stgp, 'n', stgpnum);
        stgpdict[stgpnum] = stgp;

        if (bkt.BracketType != BracketSub)
        {
            for i = bkt.TopStaveNum to bkt.BottomStaveNum + 1
            {
                libmei.AddChild(stgp, staffdict[i]);
                stgprevidx[i] = stgpnum;
            }
        }
        stgpnum = stgpnum + 1;
    }

    // finally, we need to put them all in the right order.
    stgpnum_added = CreateSparseArray();
    for j = 1 to numstaff + 1
    {
        if (stgprevidx.PropertyExists(j))
        {
            // there is a group
            stgpnum = stgprevidx[j];
            if (utils.IsInArray(stgpnum_added, stgpnum) = false)
            {
                libmei.AddChild(parentstgrp, stgpdict[stgpnum]);
                stgpnum_added.Push(stgpnum);
            }
        }
        else
        {
            libmei.AddChild(parentstgrp, staffdict[j]);
        }

    }
    return parentstgrp;
}  //$end

function GenerateLine (bobj) {
    //$module(ExportGenerators.mss)
    line = null;
    bar = bobj.ParentBar;

    switch (bobj.Type)
    {
        case ('Slur')
        {
            line = libmei.Slur();
            slurrend = ConvertSlurStyle(bobj.StyleId);
            libmei.AddAttribute(line, 'rend', slurrend[1]);
        }
        case ('CrescendoLine')
        {
            line = libmei.Hairpin();
            libmei.AddAttribute(line, 'form', 'cres');
        }
        case ('DimuendoLine')
        {
            line = libmei.Hairpin();
            libmei.AddAttribute(line, 'form', 'dim');
        }
        case ('OctavaLine')
        {
            line = libmei.Octave();
            octrend = ConvertOctava(bobj.StyleId);
            libmei.AddAttribute(octl, 'dis', octrend[0]);
            libmei.AddAttribute(octl, 'dis.place', octrend[1]);
        }
        case ('Trill')
        {
            line = GenerateTrill(bobj);
            // NB: Return here since the trill already has its properties set.
            return line;
        }
        case ('Line')
        {
            // a generic line element.
            linecomps = MSplitString(bobj.StyleId, '.');
            switch(linecomps[2])
            {
                case('bracket')
                {
                    line = libmei.Line();
                    libmei.AddAttribute(line, 'type', 'bracket');
                    if (linecomps.Length > 4)
                    {
                        if (linecomps[4] = 'start')
                        {
                            libmei.AddAttribute(line, 'subtype', 'start');
                        }

                        if (linecomps[4] = 'end')
                        {
                            libmei.AddAttribute(line, 'subtype', 'end');
                        }
                    }
                }
            }
        }
    }

    if (line = null)
    {
        return null;
    }

    line = AddBarObjectInfoToElement(bobj, line);

    return line;
}  //$end

function GenerateTrill (bobj) {
    //$module(ExportGenerators.mss)
    /* There are two types of trills in Sibelius: A line object and a
        symbol object. This method normalizes both of these.
    */
    trill = libmei.Trill();
    voicenum = bobj.VoiceNumber;
    bar = bobj.ParentBar;

    if (voicenum = 0)
    {
        warnings = Self._property:warnings;
        warnings.Push(utils.Format(_ObjectAssignedToAllVoicesWarning, bobj.Type));
        voicenum = 1;
    }

    obj = GetNoteObjectAtPosition(bobj);

    if (obj != null)
    {
        libmei.AddAttribute(trill, 'startid', '#' & obj._id);
    }

    trill = AddBarObjectInfoToElement(bobj, trill);

    return trill;
}  //$end


Remove dead code

Remove dead code

Was added in a WIP commit (7ffe10b), but it was never used

function ProcessScore () {
    //$module(ExportProcessors.mss)
    // processors are a bit like a workflow manager -- they orchestrate the
    // generators, which in turn use the converters to convert specific values from sibelius
    // to MEI.
    score = Self._property:ActiveScore;

    mei = libmei.Mei();
    libmei.setDocumentRoot(mei);

    libmei.AddAttribute(mei, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
    libmei.AddAttribute(mei, 'xmlns', 'http://www.music-encoding.org/ns/mei');
    libmei.AddAttribute(mei, 'meiversion', '4.0.1');

    header = GenerateMEIHeader();
    libmei.AddChild(mei, header);

    music = GenerateMEIMusic();
    libmei.AddChild(mei, music);

}  //$end

function ProcessBeam (bobj, layer, normalizedBeamProp) {
    //$module(ExportProcessors.mss)
    /*
        Returns the active beam, if any, and creates new beam elements
        at the start of a beam.
    */
    switch (normalizedBeamProp)
    {
        case (NoBeam)
        {
            if (bobj.GraceNote)
            {
                if (layer._property:ActiveGraceBeam != null)
                {
                    layer._property:ActiveGraceBeam = null;
                }
                // If there is an active non-grace beam, we have a normal beam spanning
                // over a non-beamed grace note that will be added as a child of that beam.
                return GetNongraceParentBeam(bobj, layer);
            }
            else
            {
                if (layer._property:ActiveBeam != null)
                {
                    layer._property:ActiveBeam = null;
                }
                return null;
            }
        }
        case (StartBeam)
        {
            newBeam = libmei.Beam();
            if (bobj.GraceNote)
            {
                layer._property:ActiveGraceBeam = newBeam;
                nonGraceParentBeam = GetNongraceParentBeam(bobj, layer);
                if (nonGraceParentBeam != null)
                {
                    newBeam._property:ParentBeam = nonGraceParentBeam;
                }
            }
            else
            {
                layer._property:ActiveBeam = newBeam;
            }
            return newBeam;
        }
        default {
            // ContinueBeam and SingleBeam
            if (bobj.GraceNote)
            {
                return layer._property:ActiveGraceBeam;
            }
            else
            {
                return layer._property:ActiveBeam;
            }
        }
    }
}  //$end

function ProcessTuplet (noteRest, meielement, layer) {
    //$module(ExportProcessors.mss)
    parentTuplet = noteRest.ParentTupletIfAny;
    if (parentTuplet = null)
    {
        return null;
    }

    if (noteRest.GraceNote)
    {
        // In Sibelius, grace notes before the first tuplet note are part of the
        // tuplet. In MEI, we want to put them before the tuplet element.
        prevNote = PrevNormalOrGrace(noteRest, false);
        if (null = prevNote)
        {
            return null;
        }
        if (not TupletsEqual(parentTuplet, prevNote.ParentTupletIfAny))
        {
            return null;
        }
    }

    /*
       We encode inner tuplets of nested tuplet structures as <tupletSpan>s.
       Therefore we always return the outermost MEI tuplet for content to be added.
       Still, we need to keep track of the inner tuplets to assign the @endid.
       layer._property:ActiveMeiTuplet always points to the innermost tuplet.
    */
    activeMeiTuplet = layer._property:ActiveMeiTuplet;

    tupletIsContinued = activeMeiTuplet != null and TupletsEqual(parentTuplet, activeMeiTuplet._property:SibTuplet);

    if (not(tupletIsContinued))
    {
        // New tuplets need to be added
        noteRestTupletStack = GetTupletStack(noteRest);
        sibTuplet = null;
        meiTupletDepth = GetMeiTupletDepth(layer);

        for i = meiTupletDepth to noteRestTupletStack.Length
        {
            sibTuplet = noteRestTupletStack[i];
            nextActiveMeiTuplet = GenerateTuplet(sibTuplet);
            if (meiTupletDepth > 0 or i > meiTupletDepth)
            {
                // We have an inner tuplet that we encode as <tupletSpan>
                nextActiveMeiTuplet = ShiftTupletToTupletSpan(nextActiveMeiTuplet, layer);
            }
            libmei.AddAttribute(nextActiveMeiTuplet, 'startid', '#' & meielement._id);
            // We basically create a linked list of tuplets
            nextActiveMeiTuplet._property:ParentTuplet = activeMeiTuplet;
            nextActiveMeiTuplet._property:SibTuplet = sibTuplet;
            activeMeiTuplet = nextActiveMeiTuplet;
        }
        layer._property:ActiveMeiTuplet = activeMeiTuplet;
    }

    meiTuplet = activeMeiTuplet;
    for i = 0 to CountTupletsEndingAtNoteRest(noteRest)
    {
        libmei.AddAttribute(meiTuplet, 'endid', '#' & meielement._id);
        meiTuplet = meiTuplet._property:ParentTuplet;
    }
    layer._property:ActiveMeiTuplet = meiTuplet;

    outermostMeiTuplet = activeMeiTuplet;
    while (outermostMeiTuplet._property:ParentTuplet != null)
    {
        outermostMeiTuplet = outermostMeiTuplet._property:ParentTuplet;
    }
    return outermostMeiTuplet;
}  //$end

function ShiftTupletToTupletSpan (tuplet) {
    //$module(ExportProcessors.mss)
    /*
        Shifts the tuplet object to a tupletSpan object. This is
        useful if we need to define a tuplet outside of a hierarchy;
        for example, if a tuplet is only on part of a beam group.

        A tupletSpan supports many of the same attributes as a tuplet
        object, so we can copy the attributes verbatim. Functionally,
        it should behave the same way as a tuplet, except that it
        takes no child elements and the parent element is the measure,
        not the layer. These two things get handled higher up the
        processing chain, however, so we don't have to worry about them here.
    */

    tupletSpan = libmei.TupletSpan();
    libmei.SetAttributes(tupletSpan, tuplet.attrs);

    if (tuplet._parent != null)
    {
        // if the tuplet has already been added to the tree,
        // remove it now. (Unfortunately, we can't delete the object,
        // but disowning it from the tree should be enough to keep it from
        // appearing in the output.)
        pobj = libmei.getElementById(tuplet._parent);
        libmei.RemoveChild(pobj, tuplet);
        tuplet._parent = null;
    }

    mobjs = Self._property:MeasureObjects;
    mobjs.Push(tupletSpan._id);
    Self._property:MeasureObjects = mobjs;

    return tupletSpan;
}  //$end

function ProcessLyric (lyricobj, objectPositions) {
    //$module(ExportProcessors.mss)
    /*
        We need to track initial, medial, and terminal lyrics. Sibelius
        only allows us to know the medial and terminal, so we store all the
        syllables in an array until we reach the end of the word, and then
        attach them to the notes.
    */

    if (lyricobj.Text = '')
    {
        return null;
    }

    styleparts = MSplitString(lyricobj.StyleId, '.');
    verse_id = styleparts[5];
    verse_id_arr = MSplitString(verse_id, false);
    verse_num = verse_id_arr[5];
    bar_num = lyricobj.ParentBar.BarNumber;
    staff_num = lyricobj.ParentBar.ParentStaff.StaffNum;
    voicenum = lyricobj.VoiceNumber;

    if (voicenum = 0)
    {
        // assign it to the first voice, since we don't have any notes in voice 0.
        voicenum = 1;
        warnings = Self._property:warnings;
        warnings.Push(utils.Format(_ObjectAssignedToAllVoicesWarning, bar_num, voicenum, 'Lyric object'));
    }

    lyricwords = Self._property:LyricWords;

    if (lyricwords.PropertyExists(staff_num) = False)
    {
        lyricwords[staff_num] = CreateDictionary();
    }

    lyricstaff = lyricwords[staff_num];

    if (lyricstaff.PropertyExists(voicenum) = False)
    {
        lyricstaff[voicenum] = CreateDictionary();
    }

    lyricvoice = lyricstaff[voicenum];

    if (lyricvoice.PropertyExists(verse_num) = False)
    {
        lyricvoice[verse_num] = CreateSparseArray();
    }

    if (lyricobj.SyllableType = MiddleOfWord)
    {
        lyricvoice[verse_num].Push(lyricobj);
        // we haven't reached the end of the word yet, so just return
        // and keep going.
        return null;
    }

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // anything past here is in the EndOfWord condition.
    // this should return a sparse array containing the syllables of the word.
    lyricvoice[verse_num].Push(lyricobj);
    lyric_word = lyricvoice[verse_num];
    objects = objectPositions[voicenum];

    obj_id = null;
    obj = null;

    for j = 0 to lyric_word.Length
    {
        syl = lyric_word[j];

        verse = libmei.Verse();
        libmei.AddAttribute(verse, 'n', verse_num);
        sylel = libmei.Syl();
        // In the case of elisions, we create multiple syl elements from one
        // LyricItem. We have to distinguish between the first and the last syl
        // element we created. In the common, simple case, both are identical.
        sylel_last = sylel;
        libmei.AddChild(verse, sylel);

        // Split syllable text by underscore, in case there is an elision
        syllables = MSplitString(syl.Text, '_');
        libmei.SetText(sylel, syllables[0]);

        // Handle any found elisions
        for s = 1 to syllables.Length
        {
            libmei.AddAttribute(sylel_last, 'con', 'b');
            sylel_last = libmei.Syl();
            libmei.SetText(sylel_last, syllables[s]);
            libmei.AddChild(verse, sylel_last);
        }

        if (syl.SyllableType = MiddleOfWord)
        {
            libmei.AddAttribute(sylel_last, 'con', 'd'); //dash syllable connector

            // New word starts at the first syllable in the lyric_word or after an elision
            if (j = 0 or syllables.Length > 1)
            {
                libmei.AddAttribute(sylel_last, 'wordpos', 'i'); // 'initial'
            }
            else
            {
                libmei.AddAttribute(sylel_last, 'wordpos', 'm'); // 'medial'
            }
        }

        // Word ends at EndOfWord or before an elision (if there are syllables before the elision)
        if (j > 0 and (syl.SyllableType = EndOfWord or syllables.Length > 1))
        {
            libmei.AddAttribute(sylel, 'wordpos', 't'); // 'terminal'
        }

        obj = GetNoteObjectAtPosition(syl);

        if (obj != null)
        {
            name = libmei.GetName(obj);

            if (name = 'rest')
            {
                warnings = Self._property:warnings;
                warnings.Push(utils.Format(_ObjectIsOnAnIllogicalObject, bar_num, voicenum, 'Lyric', 'rest'));
            }

            libmei.AddChild(obj, verse);
        }
        else
        {
            Log('Could not find note object for syl ' & syl);
        }
    }

    // Check if the last syllable is melismatic
    if (syl.NumNotes > 1)
    {
        libmei.AddAttribute(sylel_last, 'con', 'u'); // 'underscore'
    }

    // now reset the syllable array for this voice
    lyricvoice[verse_num] = CreateSparseArray();
}  //$end

function ProcessSystemStaff (score) {
    //$module(ExportProcessors.mss)
    systf = score.SystemStaff;

    for each bar in systf
    {
        for each bobj in bar
        {
            switch (bobj.Type)
            {
                case ('SpecialBarline')
                {
                    spclbarlines = Self._property:SpecialBarlines;
                    if (spclbarlines.PropertyExists(bar.BarNumber) = False)
                    {
                        spclbarlines[bar.BarNumber] = CreateSparseArray();
                    }

                    spclbarlines[bar.BarNumber].Push(ConvertBarline(bobj.BarlineInternalType));
                }
                case ('SystemTextItem')
                {
                    if (bobj.OnNthBlankPage < 0)
                    {
                        ProcessFrontMatter(bobj);
                    }

                    systemtext = Self._property:SystemText;

                    if (systemtext.PropertyExists(bar.BarNumber) = False)
                    {
                        systemtext[bar.BarNumber] = CreateSparseArray();
                    }

                    systemtext[bar.BarNumber].Push(bobj);
                }
                case ('Graphic')
                {
                    Log('Found a graphic!');
                    Log('is object? ' & IsObject(bobj));
                }
                case ('RepeatTimeLine')
                {
                    RegisterVolta(bobj);
                }
            }
        }
    }
}  //$end

function ProcessFrontMatter (bobj) {
    //$module(ExportProcessors.mss)
    /*
        For example, if page 2 (0-indexed) is the first page of music,
        then 'OnNthPage' will be 2, but the system staff items for
        the first page of text will be -2, so 2 + (-2) = 0 + 1;
    */
    bar = bobj.ParentBar;
    text = '';

    pnum = (bar.OnNthPage + bobj.OnNthBlankPage) + 1;
    frontmatter = Self._property:FrontMatter;

    if (frontmatter.PropertyExists(pnum) = False)
    {
        pb = libmei.Pb();
        libmei.AddAttribute(pb, 'n', pnum);
        frontmatter[pnum] = CreateSparseArray(pb);
    }

    text = AddFormattedText(libmei.Div(), bobj);
    frontmatter[pnum].Push(text);

}  //$end

function RegisterVolta (bobj) {
    //$module(ExportProcessors.mss)
    voltabars = Self._property:VoltaBars;
    style = MSplitString(bobj.StyleId, '.');

    if (style[2] = 'repeat')
    {
        voltabars[bobj.ParentBar.BarNumber] = bobj;
    }
}  //$end

function ProcessVolta (mnum) {
    //$module(ExportProcessors.mss)
    voltabars = Self._property:VoltaBars;

    if (voltabars.PropertyExists(mnum))
    {
        voltaElement = libmei.Ending();

        Self._property:VoltaElement = voltaElement;

        voltaObject = voltabars[mnum];
        voltainfo = ConvertEndingValues(voltaObject.StyleId);

        libmei.AddAttribute(voltaElement, 'n', voltainfo[0]);
        libmei.AddAttribute(voltaElement, 'label', voltainfo[1]);
        libmei.AddAttribute(voltaElement, 'type', voltainfo[2]);

        if (voltaObject.EndBarNumber != mnum)
        {
            Self._property:ActiveVolta = voltaObject;
        }

        return voltaElement;
    }
    else
    {
        if (Self._property:ActiveVolta != null)
        {
            // we have an unresolved volta, so
            // we'll keep the previous parentElement
            // active.
            activeVolta = Self._property:ActiveVolta;
            voltaElement = Self._property:VoltaElement;

            // if the end bar is the current bar OR if the end
            // bar is the next bar, but the end position is 0, we're escaping the
            // volta the next time around.
            if ((activeVolta.EndBarNumber = mnum) or
                (activeVolta.EndBarNumber = (mnum + 1) and activeVolta.EndPosition = 0))
            {
                Self._property:ActiveVolta = null;
                Self._property:VoltaElement = null;
            }
            return null;
        }
    }

    return null;
}  //$end

function ProcessEndingSlurs (bar) {
    //$module(ExportProcessors.mss)
    slur_resolver = Self._property:SlurResolver;
    for voiceNumber = 1 to 5
    {
        endingSlurs = slur_resolver[LayerHash(bar, voiceNumber)];
        if (endingSlurs != null)
        {
            for each slur in endingSlurs
            {
                end_obj = GetNoteObjectAtEndPosition(slur);
                if (end_obj != null)
                {
                    libmei.AddAttribute(slur._property:mobj, 'endid', '#' & end_obj._id);
                }
            }
        }
    }
}  //$end

function ProcessScore () {
    //$module(ExportProcessors.mss)
    // processors are a bit like a workflow manager -- they orchestrate the
    // generators, which in turn use the converters to convert specific values from sibelius
    // to MEI.
    score = Self._property:ActiveScore;

    mei = libmei.Mei();
    libmei.setDocumentRoot(mei);

    libmei.AddAttribute(mei, 'xmlns:xlink', 'http://www.w3.org/1999/xlink');
    libmei.AddAttribute(mei, 'xmlns', 'http://www.music-encoding.org/ns/mei');
    libmei.AddAttribute(mei, 'meiversion', '4.0.1');

    header = GenerateMEIHeader();
    libmei.AddChild(mei, header);

    music = GenerateMEIMusic();
    libmei.AddChild(mei, music);

}  //$end

function ProcessBeam (bobj, layer, normalizedBeamProp) {
    //$module(ExportProcessors.mss)
    /*
        Returns the active beam, if any, and creates new beam elements
        at the start of a beam.
    */
    switch (normalizedBeamProp)
    {
        case (NoBeam)
        {
            if (bobj.GraceNote)
            {
                if (layer._property:ActiveGraceBeam != null)
                {
                    layer._property:ActiveGraceBeam = null;
                }
                // If there is an active non-grace beam, we have a normal beam spanning
                // over a non-beamed grace note that will be added as a child of that beam.
                return GetNongraceParentBeam(bobj, layer);
            }
            else
            {
                if (layer._property:ActiveBeam != null)
                {
                    layer._property:ActiveBeam = null;
                }
                return null;
            }
        }
        case (StartBeam)
        {
            newBeam = libmei.Beam();
            if (bobj.GraceNote)
            {
                layer._property:ActiveGraceBeam = newBeam;
                nonGraceParentBeam = GetNongraceParentBeam(bobj, layer);
                if (nonGraceParentBeam != null)
                {
                    newBeam._property:ParentBeam = nonGraceParentBeam;
                }
            }
            else
            {
                layer._property:ActiveBeam = newBeam;
            }
            return newBeam;
        }
        default {
            // ContinueBeam and SingleBeam
            if (bobj.GraceNote)
            {
                return layer._property:ActiveGraceBeam;
            }
            else
            {
                return layer._property:ActiveBeam;
            }
        }
    }
}  //$end

function ProcessTuplet (noteRest, meielement, layer) {
    //$module(ExportProcessors.mss)
    parentTuplet = noteRest.ParentTupletIfAny;
    if (parentTuplet = null)
    {
        return null;
    }

    if (noteRest.GraceNote)
    {
        // In Sibelius, grace notes before the first tuplet note are part of the
        // tuplet. In MEI, we want to put them before the tuplet element.
        prevNote = PrevNormalOrGrace(noteRest, false);
        if (null = prevNote)
        {
            return null;
        }
        if (not TupletsEqual(parentTuplet, prevNote.ParentTupletIfAny))
        {
            return null;
        }
    }

    /*
       We encode inner tuplets of nested tuplet structures as <tupletSpan>s.
       Therefore we always return the outermost MEI tuplet for content to be added.
       Still, we need to keep track of the inner tuplets to assign the @endid.
       layer._property:ActiveMeiTuplet always points to the innermost tuplet.
    */
    activeMeiTuplet = layer._property:ActiveMeiTuplet;

    tupletIsContinued = activeMeiTuplet != null and TupletsEqual(parentTuplet, activeMeiTuplet._property:SibTuplet);

    if (not(tupletIsContinued))
    {
        // New tuplets need to be added
        noteRestTupletStack = GetTupletStack(noteRest);
        sibTuplet = null;
        meiTupletDepth = GetMeiTupletDepth(layer);

        for i = meiTupletDepth to noteRestTupletStack.Length
        {
            sibTuplet = noteRestTupletStack[i];
            nextActiveMeiTuplet = GenerateTuplet(sibTuplet);
            if (meiTupletDepth > 0 or i > meiTupletDepth)
            {
                // We have an inner tuplet that we encode as <tupletSpan>
                nextActiveMeiTuplet = ShiftTupletToTupletSpan(nextActiveMeiTuplet, layer);
            }
            libmei.AddAttribute(nextActiveMeiTuplet, 'startid', '#' & meielement._id);
            // We basically create a linked list of tuplets
            nextActiveMeiTuplet._property:ParentTuplet = activeMeiTuplet;
            nextActiveMeiTuplet._property:SibTuplet = sibTuplet;
            activeMeiTuplet = nextActiveMeiTuplet;
        }
        layer._property:ActiveMeiTuplet = activeMeiTuplet;
    }

    meiTuplet = activeMeiTuplet;
    for i = 0 to CountTupletsEndingAtNoteRest(noteRest)
    {
        libmei.AddAttribute(meiTuplet, 'endid', '#' & meielement._id);
        meiTuplet = meiTuplet._property:ParentTuplet;
    }
    layer._property:ActiveMeiTuplet = meiTuplet;

    outermostMeiTuplet = activeMeiTuplet;
    while (outermostMeiTuplet._property:ParentTuplet != null)
    {
        outermostMeiTuplet = outermostMeiTuplet._property:ParentTuplet;
    }
    return outermostMeiTuplet;
}  //$end

function ShiftTupletToTupletSpan (tuplet) {
    //$module(ExportProcessors.mss)
    /*
        Shifts the tuplet object to a tupletSpan object. This is
        useful if we need to define a tuplet outside of a hierarchy;
        for example, if a tuplet is only on part of a beam group.

        A tupletSpan supports many of the same attributes as a tuplet
        object, so we can copy the attributes verbatim. Functionally,
        it should behave the same way as a tuplet, except that it
        takes no child elements and the parent element is the measure,
        not the layer. These two things get handled higher up the
        processing chain, however, so we don't have to worry about them here.
    */

    tupletSpan = libmei.TupletSpan();
    libmei.SetAttributes(tupletSpan, tuplet.attrs);

    if (tuplet._parent != null)
    {
        // if the tuplet has already been added to the tree,
        // remove it now. (Unfortunately, we can't delete the object,
        // but disowning it from the tree should be enough to keep it from
        // appearing in the output.)
        pobj = libmei.getElementById(tuplet._parent);
        libmei.RemoveChild(pobj, tuplet);
        tuplet._parent = null;
    }

    mobjs = Self._property:MeasureObjects;
    mobjs.Push(tupletSpan._id);
    Self._property:MeasureObjects = mobjs;

    return tupletSpan;
}  //$end

function ProcessLyric (lyricobj, objectPositions) {
    //$module(ExportProcessors.mss)
    /*
        We need to track initial, medial, and terminal lyrics. Sibelius
        only allows us to know the medial and terminal, so we store all the
        syllables in an array until we reach the end of the word, and then
        attach them to the notes.
    */

    if (lyricobj.Text = '')
    {
        return null;
    }

    styleparts = MSplitString(lyricobj.StyleId, '.');
    verse_id = styleparts[5];
    verse_id_arr = MSplitString(verse_id, false);
    verse_num = verse_id_arr[5];
    bar_num = lyricobj.ParentBar.BarNumber;
    staff_num = lyricobj.ParentBar.ParentStaff.StaffNum;
    voicenum = lyricobj.VoiceNumber;

    if (voicenum = 0)
    {
        // assign it to the first voice, since we don't have any notes in voice 0.
        voicenum = 1;
        warnings = Self._property:warnings;
        warnings.Push(utils.Format(_ObjectAssignedToAllVoicesWarning, bar_num, voicenum, 'Lyric object'));
    }

    lyricwords = Self._property:LyricWords;

    if (lyricwords.PropertyExists(staff_num) = False)
    {
        lyricwords[staff_num] = CreateDictionary();
    }

    lyricstaff = lyricwords[staff_num];

    if (lyricstaff.PropertyExists(voicenum) = False)
    {
        lyricstaff[voicenum] = CreateDictionary();
    }

    lyricvoice = lyricstaff[voicenum];

    if (lyricvoice.PropertyExists(verse_num) = False)
    {
        lyricvoice[verse_num] = CreateSparseArray();
    }

    if (lyricobj.SyllableType = MiddleOfWord)
    {
        lyricvoice[verse_num].Push(lyricobj);
        // we haven't reached the end of the word yet, so just return
        // and keep going.
        return null;
    }

    // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // anything past here is in the EndOfWord condition.
    // this should return a sparse array containing the syllables of the word.
    lyricvoice[verse_num].Push(lyricobj);
    lyric_word = lyricvoice[verse_num];
    objects = objectPositions[voicenum];

    obj_id = null;
    obj = null;

    for j = 0 to lyric_word.Length
    {
        syl = lyric_word[j];

        verse = libmei.Verse();
        libmei.AddAttribute(verse, 'n', verse_num);
        sylel = libmei.Syl();
        // In the case of elisions, we create multiple syl elements from one
        // LyricItem. We have to distinguish between the first and the last syl
        // element we created. In the common, simple case, both are identical.
        sylel_last = sylel;
        libmei.AddChild(verse, sylel);

        // Split syllable text by underscore, in case there is an elision
        syllables = MSplitString(syl.Text, '_');
        libmei.SetText(sylel, syllables[0]);

        // Handle any found elisions
        for s = 1 to syllables.Length
        {
            libmei.AddAttribute(sylel_last, 'con', 'b');
            sylel_last = libmei.Syl();
            libmei.SetText(sylel_last, syllables[s]);
            libmei.AddChild(verse, sylel_last);
        }

        if (syl.SyllableType = MiddleOfWord)
        {
            libmei.AddAttribute(sylel_last, 'con', 'd'); //dash syllable connector

            // New word starts at the first syllable in the lyric_word or after an elision
            if (j = 0 or syllables.Length > 1)
            {
                libmei.AddAttribute(sylel_last, 'wordpos', 'i'); // 'initial'
            }
            else
            {
                libmei.AddAttribute(sylel_last, 'wordpos', 'm'); // 'medial'
            }
        }

        // Word ends at EndOfWord or before an elision (if there are syllables before the elision)
        if (j > 0 and (syl.SyllableType = EndOfWord or syllables.Length > 1))
        {
            libmei.AddAttribute(sylel, 'wordpos', 't'); // 'terminal'
        }

        obj = GetNoteObjectAtPosition(syl);

        if (obj != null)
        {
            name = libmei.GetName(obj);

            if (name = 'rest')
            {
                warnings = Self._property:warnings;
                warnings.Push(utils.Format(_ObjectIsOnAnIllogicalObject, bar_num, voicenum, 'Lyric', 'rest'));
            }

            libmei.AddChild(obj, verse);
        }
        else
        {
            Log('Could not find note object for syl ' & syl);
        }
    }

    // Check if the last syllable is melismatic
    if (syl.NumNotes > 1)
    {
        libmei.AddAttribute(sylel_last, 'con', 'u'); // 'underscore'
    }

    // now reset the syllable array for this voice
    lyricvoice[verse_num] = CreateSparseArray();
}  //$end

function ProcessSystemStaff (score) {
    //$module(ExportProcessors.mss)
    systf = score.SystemStaff;

    for each bar in systf
    {
        for each bobj in bar
        {
            switch (bobj.Type)
            {
                case ('SpecialBarline')
                {
                    spclbarlines = Self._property:SpecialBarlines;
                    if (spclbarlines.PropertyExists(bar.BarNumber) = False)
                    {
                        spclbarlines[bar.BarNumber] = CreateSparseArray();
                    }

                    spclbarlines[bar.BarNumber].Push(ConvertBarline(bobj.BarlineInternalType));
                }
                case ('SystemTextItem')
                {
                    if (bobj.OnNthBlankPage < 0)
                    {
                        ProcessFrontMatter(bobj);
                    }

                    systemtext = Self._property:SystemText;

                    if (systemtext.PropertyExists(bar.BarNumber) = False)
                    {
                        systemtext[bar.BarNumber] = CreateSparseArray();
                    }

                    systemtext[bar.BarNumber].Push(bobj);
                }
                case ('Graphic')
                {
                    Log('Found a graphic!');
                    Log('is object? ' & IsObject(bobj));
                }
                case ('RepeatTimeLine')
                {
                    RegisterVolta(bobj);
                }
            }
        }
    }
}  //$end

function ProcessFrontMatter (bobj) {
    //$module(ExportProcessors.mss)
    /*
        For example, if page 2 (0-indexed) is the first page of music,
        then 'OnNthPage' will be 2, but the system staff items for
        the first page of text will be -2, so 2 + (-2) = 0 + 1;
    */
    bar = bobj.ParentBar;
    text = '';

    pnum = (bar.OnNthPage + bobj.OnNthBlankPage) + 1;
    frontmatter = Self._property:FrontMatter;

    if (frontmatter.PropertyExists(pnum) = False)
    {
        pb = libmei.Pb();
        libmei.AddAttribute(pb, 'n', pnum);
        frontmatter[pnum] = CreateSparseArray(pb);
    }

    text = AddFormattedText(libmei.Div(), bobj);
    frontmatter[pnum].Push(text);

}  //$end

function RegisterVolta (bobj) {
    //$module(ExportProcessors.mss)
    voltabars = Self._property:VoltaBars;
    style = MSplitString(bobj.StyleId, '.');

    if (style[2] = 'repeat')
    {
        voltabars[bobj.ParentBar.BarNumber] = bobj;
    }
}  //$end

function ProcessVolta (mnum) {
    //$module(ExportProcessors.mss)
    voltabars = Self._property:VoltaBars;

    if (voltabars.PropertyExists(mnum))
    {
        voltaElement = libmei.Ending();

        Self._property:VoltaElement = voltaElement;

        voltaObject = voltabars[mnum];
        voltainfo = ConvertEndingValues(voltaObject.StyleId);

        libmei.AddAttribute(voltaElement, 'n', voltainfo[0]);
        libmei.AddAttribute(voltaElement, 'label', voltainfo[1]);
        libmei.AddAttribute(voltaElement, 'type', voltainfo[2]);

        if (voltaObject.EndBarNumber != mnum)
        {
            Self._property:ActiveVolta = voltaObject;
        }

        return voltaElement;
    }
    else
    {
        if (Self._property:ActiveVolta != null)
        {
            // we have an unresolved volta, so
            // we'll keep the previous parentElement
            // active.
            activeVolta = Self._property:ActiveVolta;
            voltaElement = Self._property:VoltaElement;

            // if the end bar is the current bar OR if the end
            // bar is the next bar, but the end position is 0, we're escaping the
            // volta the next time around.
            if ((activeVolta.EndBarNumber = mnum) or
                (activeVolta.EndBarNumber = (mnum + 1) and activeVolta.EndPosition = 0))
            {
                Self._property:ActiveVolta = null;
                Self._property:VoltaElement = null;
            }
            return null;
        }
    }

    return null;
}  //$end

function ProcessTremolo (bobj) {
    //$module(ExportProcessors.mss)
    if (bobj.DoubleTremolos = 0)
    {
        return null;
    }

    Log('Fingered tremolo: ' & bobj.DoubleTremolos);
    tremEl = libmei.FTrem();
    libmei.AddAttribute(tremEl, 'beams', bobj.DoubleTremolos);
    libmei.AddAttribute(tremEl, 'unitdur');

} //$end

function ProcessEndingSlurs (bar) {
    //$module(ExportProcessors.mss)
    slur_resolver = Self._property:SlurResolver;
    for voiceNumber = 1 to 5
    {
        endingSlurs = slur_resolver[LayerHash(bar, voiceNumber)];
        if (endingSlurs != null)
        {
            for each slur in endingSlurs
            {
                end_obj = GetNoteObjectAtEndPosition(slur);
                if (end_obj != null)
                {
                    libmei.AddAttribute(slur._property:mobj, 'endid', '#' & end_obj._id);
                }
            }
        }
    }
}  //$end