Deleting empty measures in a score

• Feb 18, 2019 - 08:30

This weekend I was trying to implement the automatic deletion of an empty measure creating a new plugin. I am able to select each one of the measures in a score but I'm not able to find the function to know if it's empty and for sure I was not able to delete one measure. Is possible to do that?

The original discussion began here
https://musescore.org/en/node/284185

Thank you very much in advance.
Marta.


Comments

The function checks the presence of notes in the measure. It works only with the first track. as an example ...

  function measureIsEmpty(measure) {
        var segment = measure.firstSegment;
        while (segment) {
              var element = segment.elementAt(0);
              if (element && element._name() === 'Chord') {
                    return false;
              }
              segment = segment.nextInMeasure;
        }
        return true;
  }

I was thinking about trying to automate something similar just last night. However, instead of just removing a single measure, I wanted to automatically strip off all empty measures the ends of scores (e.g. for use when doing batch exports of all the scores in a directory, so that I can more easily keep track of which file is which, etc. using some dedicated asset/content manager)

I'm currently stuck trying to figure out if there is any way to use the API to remove measures. There are methods to append/add things to scores or modify existing elements, but hardly ever anything to remove stuff that already exists in the score.

It's increasingly looking like I may have to either hack this into the C++ code myself (once I get the CMake generation to work on Windows) or by putting together an external tool to fiddle with the MuseScore files directly.

In reply to by Marc Sabatella

Ah, so upon further investigation, it turns out that the "remove empty trailing measures" functionality is built into the new 3.x series, but isn't in 2.x (which I'm currently using, hence why I was trying to write a plugin to do so :)

Now, in response to my own question above:
* Simply calling cmd("command-name") should trigger the command, as if you'd executed it from the UI (e.g. I confirmed using cmd("pitch-up") to replicate the up-arrow behaviour on a selected note).
* Available commands can be found in libmscore/cmd.cpp - Score::cmd()

In reply to by Marta Morticia

Hi again,

With the "measureIsEmpty" function provided by bazhenoff I have implemented this plugin. The function detects the empty measures but I cannot find the cmd order to delete them. In the meantime I tried to insert a note in the empty measures but I think I'm doing something wrong.

Please, find the the code below. Thank you for your help.


for (var track = 0; track < 1; ++track) {

             var segment = curScore.firstSegment();
             var mea = curScore.firstMeasure;
             var aux_mea;                  
             var nmea = curScore.nmeasures;
             var cont = 1;
             while (cont &lt;= nmea)
             {
                    cont++;
                    if (mea &amp;&amp; measureIsEmpty(mea))
                    {
                          aux_mea = mea;
                          console.log('===== Measure ' + cont + aux_mea + ' ====='); 
                          cmd("insert-a",aux_mea);
                          mea = mea.nextMeasure;            
                    }
                    else
                    {
                          mea = mea.nextMeasure;
                    }
             }

}

In reply to by Marta Morticia

Add note...

  function getCursor(segment){
        var cursor = curScore.newCursor();
        cursor.rewind(0);
        while(cursor.segment){
              cursor.next();
              if(cursor.segment === segment){
                    return cursor;
              }
        }
        cursor.rewind(0);
        return cursor;
  }

  function addNote(segment){
        var cursor = getCursor(segment);
        cursor.setDuration(1, 4);
        cursor.addNote(60);
  }

  addNote(measure.firstSegment);

In reply to by Marr11317

I have this at this moment, but nothing happens

onRun:
{
var cursor = curScore.newCursor();
cursor.rewind(0);

        for (var track = 0; track &lt; /*curScore.ntracks*/1; ++track) {

             var mea = curScore.firstMeasure;
             var aux_mea;

             var nmea = curScore.nmeasures;
             var cont = 1;
             cursor.rewind(0);

             console.log('===== Track ' + nmea + ' =====');

             while (mea)
             {    
                    if (mea &amp;&amp; measureIsEmpty(mea))
                    {
                          aux_mea = mea;

                          curScore.startCmd() 
                          console.log('===== Measure ' + cont + mea + ' ====='); 

                          cmd("removeElement",aux_mea);
                          curScore.endCmd()
                    }

                    mea = mea.nextMeasure;                        
                    cont++;

             }

        }

      Qt.quit();  

   }

function measureIsEmpty(measure) {
var segment = measure.firstSegment;
while (segment) {
var element = segment.elementAt(0);
if (element && element._name() == "Chord") {
return false;
}
segment = segment.nextInMeasure;
}
console.log('===== Empty measure =====');
return true;
}

The code detects the empty measures but does nothing.

Thank you so much in advance!!

In reply to by Marta Morticia

Haven't tried it, but here's how i would do it:

import QtQuick 2.1
import MuseScore 3.0

MuseScore {
version: "1.0"
description: "Plugin que borra compases vacíos"
menuPath: "Plugins.Borra vacio"

  onRun: 
  {       
        var cursor = newCursor(curScore);      
        cursor.rewind(Cursor.SCORE_START);

        var mea = curScore.firstMeasure;
        var nextMea = mea.nextMeasure;
        do {
              If (measureIsEmpty(mea))
                    removeElement(mea)

              mea = nextMea;
              nextMea = mea.nextMeasure;
        } while (mea)


      Qt.quit();  

   }

   function measureIsEmpty(measure) {
    var segment = measure.firstSegment;
    while (segment) {
          var element = segment[0];
          if (element &amp;&amp; element._name() == "Chord") {
                return false; 
          }
          segment = segment.nextInMeasure;
    }
    console.log('===== Compás vacío =====');
    return true;
    }

Invoking Qt.Quit() in a plugin neither a dialog nor a dock plugin (in which case it will destroy them) is a bad idea, because if are any dock plugins active, it will kill them -- QtQuit() destroys the single plugin environment, closing all plugins. Bet you didn't know that there can be plugins active when yours is invoked --- I think this needs fixing in the core (close my plugin only), but for a non-dialog, non-dock plugin like yours, it is unnecessary.

Other stakeholders feel free to discuss.

In reply to by Marr11317

This has nothing to do with onSelectionChanged -- ir concerns being a dock plugin, and there is nothing the poor dock plugins can do to prevent themselves from being closed by other plugins. This is about coding the latter, not the former.

In reply to by BSG

You are right about that, and using Qt.quit can actually get dangerous if things in other dock plugins haven't been saved. Take the comments plugin. It only saves on window close, so if Qt.quit() is called by some other plugin while running. it will loose all changes.
Guess it would be worth a new issue thread...

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