Iterating through measures (bars) in a staff for all staffs

• Jun 7, 2025 - 10:24

Hello,

As said in the subject, I would like to iterate through all measures (bars) in a staff for all staffs. So I asked Microsoft Copilot and it suggested something that looked sensible:

        console.log("Iterating through all measures in all staves...");
        // Iterate through all staves in the score
        for (let staffIndex = 0; staffIndex < curScore.staves.length; staffIndex++) {
            let staff = curScore.staves[staffIndex];
            console.log("Staff " + (staffIndex + 1) + ":");
            // Iterate through all measures in the staff
            for (let measureIndex = 0; measureIndex < staff.measures.length; measureIndex++) {
                let measure = staff.measures[measureIndex];
                console.log("  Measure " + (measureIndex + 1) + ":");
                // Iterate through all elements in the measure
                for (let elementIndex = 0; elementIndex < measure.elements.length; elementIndex++) {
                    let element = measure.elements[elementIndex];
                    // Check if the element is a note
                    if (element.type === Element.Note) {
                        console.log("    Note: pitch=" + element.pitch + ", duration=" + element.duration);
                    }
                }
            }
        }

Unfortunately, it does not work. In particular, in Musescore 4.4.4 on Ubuntu 20.04, staff.measures.length does not seem to exist, perhaps not even staff.measures as the error indicates:

Cannot read property 'length' of undefined

What would be a neat way to iterate through measures?

Thanks in advance and kind regards,
peter


Comments

Measures

var mez;
var seg;
var tick;

mez = curScore.firstMeasure;
while (mez) {
     seg = mez.firstSegment;
     tick = seg.tick;
     mez = mez.nextMeasure;
}

In reply to by yonah_ag

Thanks for the prompt reply. A few questions:
What is a Segment, and what is a Tick?
And how would I get hold of the notes within that measure?
If I want to build some meta data per measure, and create a map for that purpose, how would I be able to able to identify each measure.

// outer loop for all staves???
var measure = curScore.firstMeasure;
var tally = new Map([])
while (measure) {
     let notes = measure.getNotes()
     tally[measure] =  calculateMetric(notes)
     measure = measure.nextMeasure;
}
// process tally to create ideal treble clef offset per measure using some graph optimisation
// set clef for every measure for the staff in question 

In reply to by Peter Wurmsdobler

The score is sliced into time slots called segments.
Whenever an element exists at a point in time there will a segment. I think of it as a vertical line drawn through all the staves with a horizontal time position of tick.

There are 480 ticks in a ¼ note.

Every stave has 4 tracks, (corresponding to the 4 voices that it can contain). Tracks are number from zero on the first stave and continue sequentially down all staves. So a piano score of treble and bass clef would have tracks 0 to 7.

Multiple tracks can have elements at the same tick, in different staves.

In reply to by yonah_ag

Notes are an array on chord elements.
This code adds text annotations to notes in staves.
(The variables used in notes[ii].visible block are set higher up and not relevant to you query).

This code steps through segment-by-segment rather than using measure processing as I'm not interested in measures for this particular plugin.

      curScore.startCmd();
      for (var stave = staveBeg; stave <= staveEnd; ++stave) {
         cursor.staffIdx = stave;
         cursor.rewind(rewindMode);
         cursor.staffIdx = stave;
         cursor.voice = pVox;
         while (cursor.segment && (toEOF || cursor.tick < tickEnd)) {
            if(cursor.element) {
               if(cursor.element.type == Element.CHORD) {
                  var notes = cursor.element.notes;
                  for (var ii = 0; ii < notes.length; ii++) {
                     if(notes[ii].visible) {
                        var txt = newElement(Element.STAFF_TEXT);
                        txt.text = notes[ii].fret;
                        txt.color = notes[ii].color;
                        txt.placement = Placement.ABOVE;
                        txt.align = 2; // LEFT = 0, RIGHT = 1, HCENTER = 2, TOP = 0, BOTTOM = 4, VCENTER = 8, BASELINE = 16
                        txt.fontFace = pFont;
                        txt.offsetX = pXoff;
                        if (notes[ii].small) {
                           txt.offsetY = pYspc * notes[ii].string + sYoff;
                           txt.fontSize = sSize;
                        }
                        else {
                           txt.offsetY = pYspc * notes[ii].string + pYoff;
                           txt.fontSize = pSize;
                        }
                        txt.fontStyle = 1;
                        txt.autoplace = false;
                        notes[ii].color = "#ffffff";
                        cursor.add(txt);
                     }
                  }
               }
            }
            cursor.next();
         }
      }
      curScore.endCmd();

In reply to by yonah_ag

Thanks again, I will have to ingest that as it looks a bit convoluted. I feel thrown back to coding styles of the 1990s, C-like, early C++, abbreviations for variable names, globals, lists, iterating through lists with indices, indexing, magic numbers, all very error prone. Perhaps I have been spoiled in the recent years learning more modern paradigms in python, list comprehensions, iterators, generators, map/filter/reduce, etc.

I feel like pulling my hair out; what in my opinion should be such a standard use case turns out to be so tedious: iterating over notes in measures in staves. The following code seems to see measures but not notes:

        curScore.startCmd();
        for (let staffIndex = 0; staffIndex < curScore.staves.length; ++staffIndex) {
            console.log("Staff " + (staffIndex + 1) + ":");

            var measure = curScore.firstMeasure;
            while (measure) {
                console.log("measure:");
                for (var i in measure.elements)
                    if (measure.elements[i].type == Element.NOTE)
                        console.log("note:" + measure.elements[i].tpc);

                measure = measure.nextMeasure;
            }
        }
        curScore.endCmd();

What am I missing?

Once I get through that I promise I will write up a page for newbies like me with simple concepts and boiler plate code to do some basic operations.

In reply to by yonah_ag

My impression is that objects are being referenced at various stages. Say a score allows iterating over all segments means that the score object maintains a list of segments (or a linked list); each segment may contain a list of elements, perhaps as an alias, but also contains a list of tracks, then chords, and what have you. The object model is very, very confusing, even after reading https://musescore.org/en/developers-handbook/references/musescore-inter… several times. A diagram would help me greatly, or some example score with a visualisation of an object hierarchy, differentiating between child and parent references and aggregation.

In reply to by Peter Wurmsdobler

So I installed Qt6 & QtCreator, git cloned MuseScore at commit 0342e5cc8d2afcbceb938897543b588a7fc5d4ab on master, and spent some time studying the source code. There I can see as explained more or less in https://musescore.org/en/developers-handbook/references/musescore-inter…

As defined in score.h the Score contains Measures as:

    MeasureBaseList m_measures;            // here are the notes

where MeasureBaseList contains a tick-keyed multi-map from tick to a collection of measure-base(s), either a single Measure (a bar?) and/or multiple boxes.

Being only interested in notes, as defined in measure.h, a real Measure contains Segments as:

SegmentList m_segments;

which is a container for Segment objects which in turn are EngravingItems and can be of various SegmentTypes. The ChordRest is of interest to me.

Finally, the Chord contains std::vector m_notes, so all fine.

Suppose, I have a single 4/4 staff with 2 measures, and in each say 4 1/4 C4 notes, I would expect the following:
- m_measures contains two Measure objects, one at tick 0, and one at tick 480*4.
- the first Measure object contains a segment list with 4 Segments
- each such segment would have the appropriate tick offset from the beginning of the measure
- each such segment would contain one chord containing each a C4 note.

Consequently, it should be possible to iterate over the measures in the score, in each measure over the segments, in each segment of type chord over the notes. Does this make sense?

In reply to by yonah_ag

So, to iterate through all notes in the score, you might use something like this pseudocode?

For Each segment in score
    For Each element in segment
        If element.Type = "ChordRest" Then
            For Each subelement in element
                If subelement.Type = "Note" Then
                    Do Stuff
                End If
            Next subelement
        End If
    Next element
Next segment

Say, I've never been able to include indented text in the forums without replacing it with dots as above. Is there some way to do that???

[Later] Either four spaces or tabs just give me non-indented text (as above). The spaces are simply dropped *pout*

[Still later] HOORAY!!! I didn't have four spaces before the first and last lines.

In reply to by TheHutch

The TAB Walk example plugin, (linked above), fleshes out your pseudo code and also processes segment annotations as well chords and rests. It also deals with the different segment types separately.

It should work with non TAB scores but there won't be any string or fret data.

In reply to by yonah_ag

Here's a way of processing measure by measure then segment by segment. The 'magic number' 512 should be changed to the CHORDREST enum but I can't recall what it is. This code process the first 4 tracks of a score, i.e. the first stave.

  var seg;  // segment object
  var elm;  // element object
  var note; // note object
  var tick;  // segment tick
  var tt = 0; // track index
  var nn = 0; // note index
  var anno; // annotation e.g. stave text, chord symbol
  var mez = curScore.firstMeasure; // first measure in score

  while (mez) {
     seg = mez.firstSegment;
     while (seg) {
        if (seg.segmentType == 512) {  // CHORDREST
           tick = seg.tick;
           if (seg.annotations.length > 0) {
              for (var aa in seg.annotations) {
                 anno = seg.annotations[aa];
              }
           }
           for (tt = 0; tt < 4; ++tt) { // only first 4 tracks
              elm = seg.elementAt(tt);
              if (elm) {
                 if (elm.type == Element.CHORD) {
                 for  nn in elm.notes) {
                    note = elm.notes[nn];     
                 }  
                 else if (elm.type == Element.REST) { 
                 // process rests here
                 }
              }
           }
        }
        seg = seg.nextInMeasure;
     }
     mez = mez.nextMeasure;
  }

In reply to by yonah_ag

Dear @yonah_ag, that was a very good example indeed; together with looking at the source code the structure became much clearer.
The next bit is only to work out the calls, objects and properties available through the API; there the documentation is a bit lacking, but the C++ source does help.

Many thanks to all for very helpful advice. I have written up my current understanding, together with some diagrams and a simple plugin to walk through measures and segments so far; to be enhanced soon. See https://github.com/PeterWurmsdobler/musescore/tree/main/basicmuse for more details.

One aspect of MuseScore QML programming (in VSCode) I still find tedious is the lack of documentation for API calls, types and so on. I try to work with Qt creator at the same time, looking in the header files for what might be exposed to the API.

In reply to by Peter Wurmsdobler

That's a nice document.

There are a couple of other iteration methods that I use.

1) Sometimes I process staves without any reference to a measure loop by simply using the segment.next property in a segment loop. Then I'll typically process tracks as an inner loop.

From previous discussion with other users I understand that these loops could be swapped to tracks (outer) and segments (inner). It just depends on what feels more natural for the context.

2) Sometimes I don't process by stave at all but by track, with track number starting at 0 on stave 0 and then 4 tracks per stave down through all staves. A diagram showing tracks would also be useful.

Example, a 2 stave piano + 1 stave guitar tab could be processed as tracks 0-11.

In reply to by yonah_ag

Thanks, I hope the simple demo will be helpful for others, too.
As for traversing elements within segments, my impression is that there is no simple mechanism such as:

var element = segment.firstElement;
while (element) {
    console.log("element");
    // process element, e.g. filter for type, track, staff, voice, etc.
    element = element.nextElement;
}

as an inner loop within a segment, at least what I can tell from the public methods in segment.h. It would make sense to me as the track index (i.e. staff and voice indices) are "properties" of the elements and elements are the children of the segment (with segments being children of a measure, and a measure being the value of a score map.)

More generally speaking, I do see that MuseScore uses the quite low level concepts of linked lists etc. In more modern paradigms, that would be nicer (language permitting) to express something like:

chords = segment.elements.filter(element => element.type === Muse.ElementType.CHORD);

Update: I also posted a feature request on https://musescore.org/en/node/379664

In reply to by yonah_ag

Yes but, no but, or more seriously, I do recognise that a Segment "slices" through all tracks as Segment docs say:

//    A segment holds all vertical aligned staff elements.
//    Segments are typed and contain only Elements of the same type.

The second line is a strong indication that Segment ought to be a C++ template class; for some reason however this class stores the SegmentType; the elements of the same type (linked to the SegmentType) are stored in its member m_elist as:

std::vector<EngravingItem*> m_elist;         // EngravingItem storage, size = staves * VOICES.

So the segment could simply have a getter available through the plugin API (const and non const):

    const std::vector<EngravingItem*>& elements() const { return m_elist; }
    std::vector<EngravingItem*>& elements() { return m_elist; }

in addition to the elementAt(track_idx_t track) which simply returns the element at the index in the m_elist.

One should still be able to get the track index from the EngravingItem's property if it was exposed to the plugin API:

    track_idx_t track() const;

This means that the track index is both explicitly available for every EngravingItem , but also implicitly as an index into a private array in the Segment; this redundancy has to be maintained and is error-prone.

In summary, given the current code base, it is conceivable to get a list of all elements in the segment and then process those based on some filters to the author's liking.

In reply to by Peter Wurmsdobler

I suppose that since MuseScore has been in development for many years some things persist for historical reasons that might be done differently if the software was re-developed from scratch.

I wonder if redundancy situations, (like the one above), could be involved in the apparently random score corruptions that continue to be a regular topic on the forum.

In reply to by yonah_ag

A quick google search reveals: "Templates were introduced in Release 3.0 of the language, in October 1991" (https://belaycpp.com/2021/10/01/history-of-c-templates-from-c-style-mac…), so quite some time ago.

It is a difficult problem to have the same information available at different levels of an object model: either copies of that information with some mechanism to keep it consistent, or only in one place and derive as needed, with some performance implications.

After this long discussion I now do have a way to iterate through the score and print out certain properties. However, it appears iterating through all, including notes, does not mean that I can actually change properties of these objects, e.g. the note colour. Can only properties of selected elements be changed?

If that is the case, and if I collect the selected notes into a list, is there a way to get the staff index for a given note? Given that a Note derives from an EngravingItem and the latter has a public property staffIdx this should be possible (if exposed through the API).

In reply to by Peter Wurmsdobler

If you have a note object in a variable then you can change many of its properties, including colour. I don't think that it matters whether you arrive at your note object by iterating through the score or by building an array of note objects to process. I have plugins which change many note properties without first making a selection.

Note is a sub-type of Element.
Element has a Track property from which you could calculate Staff Index.

In reply to by Peter Wurmsdobler

• The number of voices per stave is always 4.
• The number of tracks per stave is always 4.

• Voices number from 0 to 3 for each stave.
• Tracks number from 0 to N from the first stave,
where N = 4S - 1, and S = number of staves.

Hence

staffIdx = (track / 4)
voice = (track % 4)

Therefore track alone is sufficient for all 3 properties.

In reply to by yonah_ag

Thanks, having studied the source code I could verify that the number of voices per staff is always 4. However, it is not good practice to expose a number which is baked into the internals, e.g. as a vector of pointers at a fixed size of 4, pre-populated with null-pointers. Rather, the API should be one of two:
- expose the number of voices per staff as symbol, e.g. Score.VoicesPerStaff which could be saved with a score if it every changed and made available to QML transparently;
- do not expose the number of voices per staff at all, do not use a fixed size vector of pointers internally, but allow the voices to grow, then make voice and track available as properties.

In reply to by Peter Wurmsdobler

That would certainly make sense but I guess that MuseScore is keeping to the common standard in music software of always having 4 voices per stave. I mainly only use 1 or 2 voices but it could be useful to have 6 voices, e.g. for a classical guitar where each of the 6 strings can ring independently.

MuseScore doesn't seem to save any null values for empty voices, (you can check by saving in mscx format rather than in mscz), but maybe it wastes runtime memory for them in the object model.

In reply to by yonah_ag

Musescore would probably not serialise a vector of nulls to file; a segment is however initialised with an allowance for 4 voices per staff in Segment.cpp lines 260ff:

    size_t staves = score()->nstaves();
    size_t tracks = staves * VOICES;
    m_elist.assign(tracks, 0);

where m_elist is the vector of pointers to EngravingItems.

Do you still have an unanswered question? Please log in first to post your question.