Advancing the Cursor for insertion

• Jul 5, 2015 - 18:14

I thought I would attempt to re-create the plugin for HalfTime plugin for MuseScore 2.0. For each rest or note in a selection, I want to replace it by a note or rest half its length, with the entire replacement for the selection also being half the length.

I tried finding a way to remove or replace an existing Chord and I couldn't find one. Since most of the music I would apply this to is on Voice 1, I thought I would just add new Chords to Voice 2. I clone the Chord, set its duration to 1/2 of the current Chord, add it at a Cursor position and try to advance the Cursor.

I use one Cursor for Voice 1 and one Cursor for Voice 2. Both are rewound to the beginning of the selection. I add the new Chord to Voice 2 using the second Cursor. A Chord is added, but it appears to retain the original duration. I then advance both Cursors. The first Cursor advances to the next chord, the second Cursor does not advance—it appears not to understand that a note was added. The problem may be that the Cursor was created from a selection with an empty Voice 2.

Here's my entire plugin so far. I'm hoping there are some methods that I am not aware of that I can use to solve my problem. One awkward part of the plugin interface is that it appears that you read duration in ticks, but you set them with setDuration() as numerator, denominator. I am trying to use a numerator of 1 and a denominator of ticks / 120.

Thanks for any assistance.

// Halve the duration of every note in the selection

import QtQuick 2.0
import MuseScore 1.0

MuseScore {
      version:  "1.0"
      description: "TBD"
      menuPath: "Plugins.Notes.Halve Duration"

      // Halve note durations

      function halveDuration()
      {
            // Find out where to start making our changes. If a selection is present, we start from the
            // beginning of the selection. If not, we don't do anything

            var cursor = curScore.newCursor();
            cursor.rewind(1);

            if (!cursor.segment) Qt.quit(); 
            var startTrack = cursor.track;

            // Find the end of the region we will alter
      
            cursor.rewind(2);
            var endTrack = cursor.track;
            var endTick = cursor.tick;
            if (endTick == 0) endTick = curScore.lastSegment.tick + 1;
            console.log(startTrack + " - " + endTrack + " - " + endTick);

            var newCursor = curScore.newCursor();
            var newTrack;

            // Process one track at a time

            for (var track = startTrack; track <= endTrack; track += 4) {
                  console.log("\nChecking track " + track);
                  cursor.rewind(1);
                  newCursor.rewind(1);
                  newTrack = track + 1;
                  newCursor.track = newTrack;

                  // Process each segment

                  while (cursor.segment && cursor.tick < endTick) {
                        console.log("Cur Tick: " + cursor.tick);
                        console.log("New Tick: " + newCursor.tick);
                        var element = cursor.segment.elementAt(track);
                        var newTick = 0;

                        if (element) {
                              switch (element.type) {
                              case Element.CHORD:
                                    var newChord = element.clone();
                                    console.log("Found CHORD. Duration = " + element.duration);
                                    newChord.duration = element.duration / 2;
                                    newTick = element.duration;
                                    console.log("New chord duration = " + newChord.duration);
                                    newCursor.setDuration(1, cursor.tick / 240);
                                    newCursor.add(newChord);
                                    break;
                              case Element.CHORDREST:
                                    var newRest = element.clone();
                                    console.log("Found CHORDREST. Duration = " + element.duration);
                                    newRest.duration = element.duration / 2;
                                    newTick = element.duration;
                                    console.log("New rest duration = " + newChord.duration);
                                    newCursor.setDuration(1, cursor.tick / 240);
                                    newCursor.add(newRest);
                                    break;
                              default:
                                    console.log("Not Chord or Rest");
                                    break;
                              }
                        }
                        cursor.next();
                        newCursor.next();
                  }
            }
      }
          
      onRun: {
            console.log("hello halveDuration");

            if (typeof curScore !== 'undefined') {
                  halveDuration();
            }
            Qt.quit();
      }
}

Here's the debug output when I select four quarter notes from one measure:

Debug: hello halveDuration
Debug: 0 - 3 - 1920
Debug: 
Checking track 0
Debug: Cur Tick: 0
Debug: New Tick: 0
Debug: Found CHORD. Duration = 480
Debug: New chord duration = 240
Debug: Cur Tick: 480
Debug: New Tick: 0
Debug: Found CHORD. Duration = 480
Debug: New chord duration = 240
Debug: Cur Tick: 960
Debug: New Tick: 0
Debug: Found CHORD. Duration = 480
Debug: New chord duration = 240
Debug: Cur Tick: 1440
Debug: New Tick: 0
Debug: Found CHORD. Duration = 480
Debug: New chord duration = 240

Here's what the score looks like. Before running the plugin, only Voice 1 exists. Afterwards (this screen shot), you can still see Voice 1 and one quarter not for Voice 2 that was added by the plugin.

Clipboard01.png


Comments

Really? No one knows how to do something this basic?

I noticed there is another thread talking about problems inserting chords, so maybe this is not doable at the moment. Or maybe you can't insert chords using a cursor positioned to a selection. Oh, well—MuseScore 2 is a great program and a real pleasure to use. The plugin interface looks like it is just lacking attention.

In reply to by Marc Sabatella

Really? You mean that even the people who developed the plugin have no experience doing what I'm trying to do (and what I'm trying to do seems pretty basic) and it's going to be the blind leading the blind until some lucky person "gets used to" the plugin code?

MuseScore developers might not be familiar with the plugin interface, but since (I believe) it is supposed to use the same objects available internally, they might be able to spot the problem with the code I tried even if they are not used to it.

Oh, well, I'm willing to help out MuseScore by contributing plugins, but my experience so far is that the plugin architecture is buggy, incomplete and undocumented. I have reluctantly given up on producing plugins, but if, someday, someone gets "used to it", maybe they can help me out.

I've written a plugin for Photoshop (considered a tough task), written modules for Drupal and extended other software, so it isn't like I'm a beginner trying to extend a software product for the first time.

Don't get me wrong—the MuseScore developers are volunteers who can work on whatever they like and if they choose not to work on creating a usable, documented, working plugin architecture, I support them 100% (it is a lot of work). MuseScore overall is a fantastic tool and I think they have done great work.

In reply to by Marc Sabatella

The "current wisdom" is that, no, inserting new chords in a score is not possible with the current plug-in framework (see this forum thread for more details).

There is an addNote function but even this has its share of problems (as shown in that same forum thread).

I cannot but share the same feelings as the OP, as I have on other occasions noted that the work on the plug-in framework is somehow lagging behind and the efforts in this directions (see the several github PR's) seem uncoordinated and considered more or less collateral.

I hope it is not out of topic to take the occasion to repeat that the plug-in framework is an important part of the project: while it is expected that only a small fraction of the MuseScore users is willing (or able) to create new plug-ins, a larger plug-in base would benefit a great number of users (almost anyone has the task too specialized or too unusual to be even included in the code base, but implementable with a plug-in if...).

A larger plug-in base would also make easier to create new plug-ins even for non-initiated (or less-initiated), by adapting or crossing the existing ones.

So, I hope that for 2.1 there will be a more coordinated effort on plug-in framework.

Thanks,

M.

Having done the original HalfTime Plugin (stolen from an earlier Plugin and scribbled on a napkin rather than being thoroughly thought out) I did have a bash at porting it to 2.x but, as you've discovered, it's not easy and so I gave up. I'm not a developer or programmer, just a dabbler.

Initial steps required:
Read the original chords with their positions, pitches and durations
Store the chords in sequence (Push to a stack or store in an array)
Create a new Score (better IMHO than writing to a different Voice in the original score)
Pull the chords from the array and write them in sequence with their durations halved

Possible modifications (once working):
Allow user-selected duration change (2/3, 1/2, 1/3, 1/4, 3/2, 2, 3 etc.)
- ensure derived durations/tick values are integers.
Allow to work on multiple Voices and Staves
Offer to adjust Time Signature automatically or write new notes in same TS or just write to a nominal 4/4 score from which the notes can be copied and pasted
Allow option of creating in new score or of replacing original selection (only once mature)

Hi guys -
After several hours, I found the following code fixes the cursor movement issue, where 'i' is the number of elements from the start of the score to the point you want to be. You have to keep track of how far forward you are for this to work.
cursor.rewind(0);
for (var j = 0; j <= i; j++) cursor.next();
Also, the following code fixes the chord/rest insertion problem. By first adding a random note, you can then replace it with whatever chord/rest you want, and the measure will play correctly.
cursor.setDuration(dur, 128);
cursor.addNote(60);
cursor.add(newchord);
Based on this, I can/will upload doubletime.qml for MS 2.0.

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