Can a plugin access on-time/off-time?

• Jul 3, 2019 - 15:40
Reported version
3.2
Type
Plugins
Frequency
Once
Severity
S5 - Suggestion
Reproducibility
Always
Status
closed
Regression
No
Workaround
No
Project

Can I write a plugin to impart a desired phrasing upon a run of notes?

For more detail on this request see these forum posts in order:

This is the original post:

https://musescore.org/en/node/291414

This post continues the discussion in the are of implementation:

https://musescore.org/en/node/291519

-Dale


Comments

Status PR created active

This concern was posted in PR# 5190:

Why do you use exactly the first note event (both here and in libmscore)?

Some notes may have more than one note event, for example notes with ornaments applied. If your intention is to add an ability to allow a more complete control over playback for plugins then it may be worth to expose play events to plugins directly in some way. I am not sure how would it be better to implement that but otherwise only a limited functionality will be exposed, and it is not clear in which way that functionality should actually be limited.

In reply to by DLLarson

I don't know "fer sure" what "NoteEvents" are, other than from the obvious conclusion from their name, possible plurality, and demetrio's comment. I suspect you have to "look through all of them that are on this note", find the one that relates to "off-time" (or "on-time") and deal with it in its own terms. Whatever it is, the PRE must do it somehow. I'm not ready to sign on to "why don't we address all properties or event types for visibility" right yet. I have a vested interest in seeing these two visible, for they they make the difference between mechanical performance and phrased performance. Your thoughts?

In reply to by BSG

NoteEvents encapsulate the following values:

class NoteEvent {
int _pitch; // relative pitch to note pitch
int _ontime; // 1/1000 of nominal note len
int _len; // 1/1000 of nominal note len
:
};

We have been working with the last two.

I'm not sure how pitch works its way into the class, if only notes contain NoteEvents and notes already have pitch. This seems redundant but I really don't know.

If only these two properties are exposed it might be simpler to just provide an index argument that defaults to zero if not specified (if QML allows default parameter values.)

-Dale

In reply to by BSG

That question is, as they say, beyond my pay grade. :)

It would be good to have a subject matter expert to weigh in. It seems to have a tight tie to midi as I see many references to NoteEvent's from within that code.

-Dale

I suspect sooner rather than later I will simply have to start building MS to figure out what has to be developed and how much must change. The NoteEvent paradigm seems very odd in that we can see little "noticules" for ornamentation in the Piano Roll Editor, but not edit them, and you can't see them at all in the MSCX/Z, but nondefault on/off time are fully visible -- the ornament is only saved as "macro form" (i.e., "there is a Koffeeschlag mit doppeltem Cacao here") - so the PRE and the MSCX seem already to reveal a design conflict, and if NoteEvents were to be exposed to qml, the same restriction would have to be imposed, and documentation would be a mess. But what I want is profoundly useful, and a way must be found by someone. I would like to hear from demetrios or someone else on a first-name basis with NoteEvents.

How about this (input from someone knowledgeable needed - I have the sources, but someone else could answer faster). Are there any kinds of NoteEvents other than ornamentation and basic note on/off time? If not, then a consistent solution might be: If there is only one note event , you can change its on time or off time as per DLL's PR. If there are more than one, then, as in the PRE, you can't change the values, and perhaps the "get" should return null. This would be basically consistent with the PRE behavior.

In reply to by BSG

>Do we know how to ping demetrio, or explore the responsibility chain?

The "we" that's "me" doesn't. That's why I've been leaving the "plea's" at various places.

In fact, I just became aware of MuseScore recently and, being a programmer/engineer type, fixed a few things having to do with a tin whistle tab plugin I liked that didn't work well in v3.0 release. A classic open source contribution.

Once agreement on this feature is reached I'm happy to adapt my code to the agreed upon implementation.

-Dale

Actually Piano Roll Editor is already able to edit each note event separately — at least via its bottom panel where all the properties of NoteEvents mentioned above can be adjusted. I believe if we want to allow plugins to have a better control over playback we should probably expose those NoteEvents to them too, otherwise the solution will be rather limited. However my concern here is that note events may not be properly created to the time when a plugin gets called. It is also true for the piano roll editor: try to open it on a score with ornamented notes before triggering any playback, and you will not see any note events that correspond to those ornaments. Editing note events at this stage will lead to probably unintended results. So perhaps using this API should somehow trigger creating note events for the whole score or some its part. This all is possible to do but the implementation should probably be especially accurate with handling these issues to avoid both possible errors and possible extra performance penalty.

Given the aversion of the "powers that be" to enhancing the playback features of the product, or even introducing more UI to control playback, I think the possibility of plugin control over playback is a necessity.

> we should probably expose those NoteEvents

For my clarification, to expose NoteEvent's as free-standing QML objects we would need to create said objects internally along with the ownership enum of PLUGIN? Then these could be added to an existing NoteEvent list (setting ownership to SCORE)?

Is list ordering important for this item?

-Dale

In reply to by BSG

They aren't owned by a Note until it is added to the NoteEvent list. Until then the PLUGIN owns them.

It's like creating a note element with the 'newElement()' method. Until you add it to a Chord it's not owned by the Score. This is needed to ensure memory is cleaned up properly.

It's purely an internal C++ issue though.

-Dale

Hi everyone. I've chewing on this issue for a while and decided to take a stab at the more generalized object model.

The result is I've have submitted a pull request that exposes a note's NoteEvent list along with NoteEvent objects. You can find it here:

https://github.com/musescore/MuseScore/pull/5224

I've attached my test QML plugin that illustrates its use.

The PR's commit is "tagged" such that it will keep the Windows build artifacts from Appveyor so you can test these changes on Windows before they make it to the "master" branch. Unfortunately there is no Mac equivalent so if that's required you'll have build it yourself. :(

Two things aren't addressed at this point in the PR:
* How to add NoteEvent's and,
* How to remove NoteEvents.

I'm looking for ideas for those.

Note also that with the current patch you can change the various NoteEvent values and the undo stack will track it.

A side question:

I ran my test plugin on several complicated multi-instrument scores I download from the MuseScore.com site and none of them have more than one NoteEvent in their NoteEvent lists.

Can anyone supply me with a test score that actually uses more than one NoteEvent per note?

-Dale

Attachment Size
walk-note-events.qml 3.46 KB

In reply to by BSG

Hi,

When I run my new test plugin (attached) I see this output:

Plugin Details:
Menu Path: Plugins.Walk Note Events
Version: 3.0
Description: This test plugin walks through all NoteEvents in a score
Requires Score
Debug: Hello NoteEvent Walker
Debug: ###### START STAFF 0 ######
Debug: Chord[0].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.noteEvents.length=1
Debug: ------noteEvents[0].pitch=0
Debug: ------noteEvents[0].ontime=1000
Debug: ------noteEvents[0].offtime=0
Debug: ------noteEvents[0].len=1000
Debug: Chord[1].notes.length=1
Debug: ===notes[0].pitch=71
Debug: ===note.noteEvents.length=1
Debug: ------noteEvents[0].pitch=0
Debug: ------noteEvents[0].ontime=1000
Debug: ------noteEvents[0].offtime=0
Debug: ------noteEvents[0].len=1000
Debug: Chord[2].notes.length=1
Debug: ===notes[0].pitch=74
Debug: ===note.noteEvents.length=1
Debug: ------noteEvents[0].pitch=0
Debug: ------noteEvents[0].ontime=1000
Debug: ------noteEvents[0].offtime=0
Debug: ------noteEvents[0].len=1000
Debug: Chord[3].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.noteEvents.length=1
Debug: ------noteEvents[0].pitch=0
Debug: ------noteEvents[0].ontime=1000
Debug: ------noteEvents[0].offtime=0
Debug: ------noteEvents[0].len=1000
Debug: ^^^^^^ END STAFF 0 ^^^^^^

There is only single NoteEvent per note. The values are the same as I see in the Piano Roll Editor.

-Dale

Attachment Size
walk-note-events-v2.qml 3.31 KB

New site rules to discourage piracy. Without a pro account, you can't download. If you send me via .org email your email address, I will email it to you. It's really trivial to produce. Just enter a couple of notes, and add those ornaments to them from the ornaments palette.

In reply to by BSG

If it hasn't changed from the original that I did download it we should be fine.

After playing the score the NoteEvent's are indeed magically created. The test plugin now shows multiple NoteEvent's per note:

Running…
Plugin Details:
Menu Path: Plugins.Walk Note Events
Version: 3.0
Description: This test plugin walks through all NoteEvents in a score
Requires Score
Debug: Hello NoteEvent Walker
Debug: Score name=IgnoreTestNoteEvents
Debug: ###### START STAFF 0 ######
Debug: Chord[0].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.noteEvents.length=4
Debug: ------noteEvents[0].pitch=-1
Debug: ------noteEvents[0].ontime=0
Debug: ------noteEvents[0].offtime=125
Debug: ------noteEvents[0].len=125
Debug: ------noteEvents[1].pitch=0
Debug: ------noteEvents[1].ontime=125
Debug: ------noteEvents[1].offtime=250
Debug: ------noteEvents[1].len=125
Debug: ------noteEvents[2].pitch=2
Debug: ------noteEvents[2].ontime=250
Debug: ------noteEvents[2].offtime=375
Debug: ------noteEvents[2].len=125
Debug: ------noteEvents[3].pitch=0
Debug: ------noteEvents[3].ontime=375
Debug: ------noteEvents[3].offtime=1000
Debug: ------noteEvents[3].len=625
Debug: Chord[1].notes.length=1
Debug: ===notes[0].pitch=71
Debug: ===note.noteEvents.length=8
Debug: ------noteEvents[0].pitch=1
Debug: ------noteEvents[0].ontime=0
Debug: ------noteEvents[0].offtime=125
Debug: ------noteEvents[0].len=125
Debug: ------noteEvents[1].pitch=0
Debug: ------noteEvents[1].ontime=125
Debug: ------noteEvents[1].offtime=250
Debug: ------noteEvents[1].len=125
Debug: ------noteEvents[2].pitch=1
Debug: ------noteEvents[2].ontime=250
Debug: ------noteEvents[2].offtime=375
Debug: ------noteEvents[2].len=125
Debug: ------noteEvents[3].pitch=0
Debug: ------noteEvents[3].ontime=375
Debug: ------noteEvents[3].offtime=500
Debug: ------noteEvents[3].len=125
Debug: ------noteEvents[4].pitch=1
Debug: ------noteEvents[4].ontime=500
Debug: ------noteEvents[4].offtime=625
Debug: ------noteEvents[4].len=125
Debug: ------noteEvents[5].pitch=0
Debug: ------noteEvents[5].ontime=625
Debug: ------noteEvents[5].offtime=750
Debug: ------noteEvents[5].len=125
Debug: ------noteEvents[6].pitch=1
Debug: ------noteEvents[6].ontime=750
Debug: ------noteEvents[6].offtime=875
Debug: ------noteEvents[6].len=125
Debug: ------noteEvents[7].pitch=0
Debug: ------noteEvents[7].ontime=875
Debug: ------noteEvents[7].offtime=1000
Debug: ------noteEvents[7].len=125
Debug: Chord[2].notes.length=1
Debug: ===notes[0].pitch=74
Debug: ===note.noteEvents.length=3
Debug: ------noteEvents[0].pitch=0
Debug: ------noteEvents[0].ontime=0
Debug: ------noteEvents[0].offtime=125
Debug: ------noteEvents[0].len=125
Debug: ------noteEvents[1].pitch=-2
Debug: ------noteEvents[1].ontime=125
Debug: ------noteEvents[1].offtime=250
Debug: ------noteEvents[1].len=125
Debug: ------noteEvents[2].pitch=0
Debug: ------noteEvents[2].ontime=250
Debug: ------noteEvents[2].offtime=1000
Debug: ------noteEvents[2].len=750
Debug: Chord[3].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.noteEvents.length=4
Debug: ------noteEvents[0].pitch=2
Debug: ------noteEvents[0].ontime=0
Debug: ------noteEvents[0].offtime=500
Debug: ------noteEvents[0].len=500
Debug: ------noteEvents[1].pitch=0
Debug: ------noteEvents[1].ontime=500
Debug: ------noteEvents[1].offtime=666
Debug: ------noteEvents[1].len=166
Debug: ------noteEvents[2].pitch=2
Debug: ------noteEvents[2].ontime=666
Debug: ------noteEvents[2].offtime=832
Debug: ------noteEvents[2].len=166
Debug: ------noteEvents[3].pitch=0
Debug: ------noteEvents[3].ontime=833
Debug: ------noteEvents[3].offtime=999
Debug: ------noteEvents[3].len=166
Debug: ^^^^^^ END STAFF 0 ^^^^^^

In order for this scripting feature to be useful it would seem we need to trigger this behavior from the plugin?

What prevents plugin changes from being wiped out on the next play?

-Dale

In reply to by DLLarson

The Plugin probably cannot mess with ornaments (neither can the piano editor), but changes to other notes are not wiped out (compare all behaviors of the PRE), but it can look at them. We have to figure out how it records the state of the ornament realization.

In reply to by BSG

The ornamentation-delay issue is (as D notes, no pun intended), being identical for the PRE and the plugin, seems a documentation issue. An API to trigger the ornament realization wouldn't be bad, but we don't know how yet.

I've exposed a Score.createPlayEvents() method to the Score object. If this is called the ornaments are realized into the Note's NoteEvent lists. Then walking the notes you can see the updated NoteEvents.

This raises a general question....

Internally the NoteEvent lists are called "playLists". Should my proposed QML interface also change "Note.noteEvents" to "Note.playEvents" for consistency? I'm thinking it should.

Also, should NoteEvent's also be called PlayEvent's in QML?

-Dale

In reply to by DLLarson

I don't like the name "NoteEvents" at all, because they aren't notes, nor events, but really little sub-notes, but there is much to be said for tracking the C++ names ipso facto. I don't feel qualified to offer an opinion beyond that.

I've pushed up the the changes.

From the QML perspective all NoteEvent's are now PlayEvent's. A mid-way compromise between the two. The new name at least ties the objects with playback activity.

The new QML interfaces are:

  • Score.createPlayEvents() - populates PlayEvent's with ornamentation effects.
  • Note.playEvents - list of PlayEvents.
  • PlayEvent.ontime [read/write] - get/set ontime.
  • PlayEvent.len[read/write] - get/set len.
  • PlayEvent.pitch[read/write] - get/set pitch offset.
  • PlayEvent.offtime[read] - get computed offtime.

Set's also create undo records.

A run of the new test plugin (attached) against your test score (also attached) produces this output:

Running…
Plugin Details:
Menu Path: Plugins.Walk PlayEvents
Version: 3.0
Description: This test plugin walks through all PlayEvents in a score
Requires Score
Debug: Hello PlayEvent Walker
Debug: Score name=IgnoreTestNoteEvents
Debug: ###### START STAFF 0 ######
Debug: Chord[0].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.playEvents.length=4
Debug: ------playEvents[0].pitch=-1
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=125
Debug: ------playEvents[0].len=125
Debug: ------playEvents[1].pitch=0
Debug: ------playEvents[1].ontime=125
Debug: ------playEvents[1].offtime=250
Debug: ------playEvents[1].len=125
Debug: ------playEvents[2].pitch=2
Debug: ------playEvents[2].ontime=250
Debug: ------playEvents[2].offtime=375
Debug: ------playEvents[2].len=125
Debug: ------playEvents[3].pitch=0
Debug: ------playEvents[3].ontime=375
Debug: ------playEvents[3].offtime=1000
Debug: ------playEvents[3].len=625
Debug: Chord[1].notes.length=1
Debug: ===notes[0].pitch=71
Debug: ===note.playEvents.length=8
Debug: ------playEvents[0].pitch=1
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=125
Debug: ------playEvents[0].len=125
Debug: ------playEvents[1].pitch=0
Debug: ------playEvents[1].ontime=125
Debug: ------playEvents[1].offtime=250
Debug: ------playEvents[1].len=125
Debug: ------playEvents[2].pitch=1
Debug: ------playEvents[2].ontime=250
Debug: ------playEvents[2].offtime=375
Debug: ------playEvents[2].len=125
Debug: ------playEvents[3].pitch=0
Debug: ------playEvents[3].ontime=375
Debug: ------playEvents[3].offtime=500
Debug: ------playEvents[3].len=125
Debug: ------playEvents[4].pitch=1
Debug: ------playEvents[4].ontime=500
Debug: ------playEvents[4].offtime=625
Debug: ------playEvents[4].len=125
Debug: ------playEvents[5].pitch=0
Debug: ------playEvents[5].ontime=625
Debug: ------playEvents[5].offtime=750
Debug: ------playEvents[5].len=125
Debug: ------playEvents[6].pitch=1
Debug: ------playEvents[6].ontime=750
Debug: ------playEvents[6].offtime=875
Debug: ------playEvents[6].len=125
Debug: ------playEvents[7].pitch=0
Debug: ------playEvents[7].ontime=875
Debug: ------playEvents[7].offtime=1000
Debug: ------playEvents[7].len=125
Debug: Chord[2].notes.length=1
Debug: ===notes[0].pitch=74
Debug: ===note.playEvents.length=3
Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=125
Debug: ------playEvents[0].len=125
Debug: ------playEvents[1].pitch=-2
Debug: ------playEvents[1].ontime=125
Debug: ------playEvents[1].offtime=250
Debug: ------playEvents[1].len=125
Debug: ------playEvents[2].pitch=0
Debug: ------playEvents[2].ontime=250
Debug: ------playEvents[2].offtime=1000
Debug: ------playEvents[2].len=750
Debug: Chord[3].notes.length=1
Debug: ===notes[0].pitch=72
Debug: ===note.playEvents.length=4
Debug: ------playEvents[0].pitch=2
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=500
Debug: ------playEvents[0].len=500
Debug: ------playEvents[1].pitch=0
Debug: ------playEvents[1].ontime=500
Debug: ------playEvents[1].offtime=666
Debug: ------playEvents[1].len=166
Debug: ------playEvents[2].pitch=2
Debug: ------playEvents[2].ontime=666
Debug: ------playEvents[2].offtime=832
Debug: ------playEvents[2].len=166
Debug: ------playEvents[3].pitch=0
Debug: ------playEvents[3].ontime=833
Debug: ------playEvents[3].offtime=999
Debug: ------playEvents[3].len=166
Debug: ^^^^^^ END STAFF 0 ^^^^^^

It's time for some wider feedback from other interested parties.

We also need feedback on the need to add destroy PlayEvent's from QML.

-Dale

Attachment Size
walk-play-events.qml 3.05 KB
IgnoreTestNoteEvents.mscz 4.58 KB

Is there any way I can talk anyone who can do Mac builds to make one for me/us??? Setting up an environment that can do that is not a trivial or guaranteed thing, and someone who can do this can just git-fetch and build one for me....TIA.

OK, OK, guys -- hold your NoteEvents! ...getting to it (writing the test plugin I need is nontrivial) -- I suppose I should first prove that DLL's works ...

In reply to by mattmcclinch

This build is very broken. Try to play any score and it either stutters infinitely on one note or sounds all notes one by one but doesn't shut them off. The note events are obviously very broken. I had to ForceQuit, twice.

Mattmcc, can you reproduce my failure>

  • The first test should simply be load the score and play.
  • Then use my QML test.
  • Then try something new.

My code changes didn't modify anything about note events internally. So the first test should simply work. The second test only does read-only scans by default (if the doWriteTest is 'false') so it also should not cause playback problems.

In those cases it's something about the Mac build or install. Note that these builds are based on top of current 'master' PR's so you should check a 'nightly' build from here to sanity check those PR's.

http://prereleases.musescore.org/macosx/nightly/

-Dale

That test has already failed. I tried to load a score and play it, and that failed (note-off's weren't observed).. Didn't get to load the plugin. I suspect something changed in either the NoteEvents, their vtables, or the dependencies and things that have to be recompiled (and not enough was recompiled) or something on that order. You did add some methods to the NoteEvents, right (could change vtables)....

>You did add some methods to the NoteEvents, right (could change vtables)....

I only exposed them via the outside Plugin layer. Unless the plugin wrappers are called to modify PlayEvent data it should have no effect on normal operation. I didn't actually extend the system's internal classes. You can see this by looking at the commit. It only affects plugin level code.

>or the dependencies and things that have to be recompiled (and not enough
>was recompiled) or something on that order.

This is more likely but I'm thinking we should look at what 'master' contributed to the result. Best to try the nightly so we can see where the issue lies.

-Dale

For what it's worth, I tried the new build again creating a new score, piano solo. The first and only note I entered would not stop, and I again had to ForceQuit the app. Interestingly, the other instance of MS I had open then started experiencing similar problems until I "reset midi".

The build that I provided should allow you to run the plugin and save the result. If there are playback issues, it is unlikely that they are the result of DLLarson's changes. You can continue to use the official MuseScore release for playback.

That's a crippling restriction -- type any alphabetic key and the app crashes.... Do you reproduce my failure (can't play any notes without crashing) or not? Were all modules through-and-through everywhere recompiled? Can you play without crashing without his changes?

I am surprised that you were able to have multiple instances of MuseScore 3 open at the same time. Perhaps that is contributing to the playback problems that you are experiencing. What happens if you close all other instances of MuseScore before running my build?

I tried running his plugin in the Plugin creator and received: Running…
Creating component failed
line 24: Expected token ;'
line 24: Expected token
;'

One testing strategy I've used with MuseScore on Windows is to have the previous supported release installed and then copy only the new test executable (MuseScore.exe for Windows) directly into the 'bin' folder and run it that way. This way the overall application installation on the OS doesn't change so your dependencies should be good.

I'm note sure how the Mac is organized but it is a Unix based OS so it probably follows a similar layout which looks much like Linux even on Windows.

-Dale

In reply to by mattmcclinch

Egg on my face; if I close the other MS before starting, it plays properly. But I have had multiple MS's open many, many times before, same and different versions, and no problem. But, indeed, we are beyond that, and sorry for the false alarm. Now have to debug why DLL's plugin doesn't load ....

Let's make sure we're all using the same test QML so we all agree on results.

I'm attaching the current working test QML to this message. This should run without issues.

Let us know if it works. Thanks!

-Dale

Attachment Size
walk-play-events-v2.qml 3.09 KB

In reply to by DLLarson

This works flawlessly! Thank you both so much. Time to do "my thing" with it ...
Running…
Plugin Details:
Menu Path: Plugins.Walk PlayEvents
Version: 3.0
Description: This test plugin walks through all PlayEvents in a score
Requires Score
Debug: Hello PlayEvent Walker
Debug: Score name=St_Anne_direct_no_SDRC
Debug: ###### START STAFF 0 ######
Debug: Chord[0].notes.length=1
Debug: ===notes[0].pitch=70
Debug: ===note.playEvents.length=1
Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=1000]]
Debug: ------playEvents[0].len=1000
Debug: Chord[24].notes.length=1
Debug: ===notes[0].pitch=51
Debug: ===note.playEvents.length=1
Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=1000
Debug: ------playEvents[0].len=1000
Debug: Chord[25].notes.length=1

[clipped for volume]

Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=850
Debug: ------playEvents[0].len=850
Debug: Chord[28].notes.length=1
Debug: ===notes[0].pitch=46
Debug: ===note.playEvents.length=1
Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=1000
Debug: ------playEvents[0].len=1000
Debug: Chord[29].notes.length=1
Debug: ===notes[0].pitch=51
Debug: ===note.playEvents.length=1
Debug: ------playEvents[0].pitch=0
Debug: ------playEvents[0].ontime=0
Debug: ------playEvents[0].offtime=1000
Debug: ------playEvents[0].len=1000
Debug: ^^^^^^ END STAFF 2 ^^^^^^

Here's a proof of concept. Life would be better if someone would point me to a simple example of how to issue a(n error) message box. This plugin is the paragon of simplicity; you select some notes in one or more staves (with the blue-box selection, click-on won't do), and invoke "mung offtimes". Set anything from 100 to 2000 and there you go. I skip over notes with multiple play-events (i.e., ornamented). Strangely, you may not hear the result take effect until you cancel the selection (way after the plugin is gone). Don't quite understand this. Undo works. Also attached is a recent chorale-prelude of mine, where the cantus/oboe part has been phrased, including single notes at times, solely with this new plugin. I wish I could accept keystrokes (Enter and Esc) to do the obvious.

This is a watershed. This is a significant leap in the usability of MuseScore as a tool for crafting automatic performances. Enjoy/feedback/thanks!
(Anybody else reading, this is only usable with the special builds described in this thread).

Attachment Size
MungOfftimes.qml 6.28 KB
St_Anne_direct_no_SDRCofft.mscz 18.76 KB

Are we to understand then that DLLarson's pull request works entirely as expected and provides all of the functionality that you are looking for?

In reply to by mattmcclinch

Well, as you see, my application is no older than your build, just born, but, "Gee whiz! It seems to work!" indeed. I was surprised that off-time was not settable (you have to use "len"), but that's small potatoes. It certainly seems to provide the functionality I was hoping for (on this round -- it's conceivable that creating and deleting play events would be nice some day, but I don't have a proposed UX use yet). For now, the answer is "yes".

In reply to by mattmcclinch

>Are we to understand then that DLLarson's pull request works entirely as expected
>and provides all of the functionality that you are looking for?

I would say no since there is no way to build a new list of PlayEvent's for a note. I'm looking at that issue right now. QML still befuddles me in how to expose automatically creatable QML objects from C++ objects.

My overall thinking is this:

Add an additional method to PlayEvents list called clear(). This already is supported by QQmlListProperty.

My implementation would clear the native NoteEvent list and add a single default NoteEvent to it. This is the default internal state of a newly created Ms::Note. Semantically it's more like a 'reset()' than 'clear()' though. There is some wiggle room here although I haven't yet found the boundaries of that room yet. ;)

I also need to be able to create a QML PlayEvent that can be append()-ed to the list--which is also supported by QQmlListProperty.

At present, I'm currently wrestling with creating a PlayEvent from QML that instantiates my internal Ms::PluginAPI::PlayEvent. The Qt docs seem to say this is possible. It's just another Qt rabbit hole to jump down.

A definitely simpler approach would be to do the PlayEvents.clear() implementation as above and also have an argument-less PlayEvents.append() method that simply adds a default NoteEvent to the list. Then the values can be manipulated as desired. This feels less "object-oriented" though.

So the process could be like:
note.playEvents.clear(); // Reduces list down to single default NoteEvent.
note.playEvents.append(); // Call n-1 times to get n-length list.
// Loop on note.playEvents to fill out the desired settings.

-Dale

In reply to by DLLarson

I could live with that (creation paradigm). But it's not as urgent as today's stuff. Re len vs offtime, ok, but I repeat that offtime, not len, should be the user-visible artifact not only here but in the Piano Roll Editor; the current situation is a UI bug AFAIAC.

So if "offtime = ontime + len", then also" ontime = offtime - len" and "len = offtime - ontime"
No reason for having any one of those read-only IMHO

Another thing becoming possible now is the arpeggiation of a right-hand chord expressed in several voices; a plugin will be able do this now, but it is impossible in MS raw. I need this every time I realize continuo on harpsichord.

In reply to by BSG

> I was surprised that off-time was not settable (you have to use "len"), but that's small potatoes.

The offtime setting is not supported in the internal NoteEvent.

As I tried to provide it myself via PlayEvent I had problems with ensuring the object was consistent. For instance, if you set an offtime that is before the current ontime what should the code do? Should it swap the two times? Should it error? ... and so on. If you set len how would it keep the offtime consistent?

I see why it was done as a base time and length. Simple to move or set duration. I did provided the offtime as a readonly property. Just like the internal version though. I can remove it if you think it causes API confusion.

-Dale

In reply to by BSG

> but I repeat that offtime, not len, should be the user-visible artifact not only here but in
>the Piano Roll Editor; the current situation is a UI bug AFAIAC.

The UI has complete control of the final state while you are interacting with it so I get what you're saying from a UI perspective. From a code/scripting perspective it's a mess.

I'm sure the UI code takes your final offtime selection and calculates the difference between the UI offtime and ontime and sets it as a new len internally.

-Dale

In reply to by Jojo-Schmitz

>So if "offtime = ontime + len", then also" ontime = offtime - len" and
>"len = offtime - ontime" No reason for having any one of those read-only IMHO

True mathematically but since only a single value can change at once what do you do with the others?

Basically you will have side effects on the other values depending on what you set since you have three values maintained from the user perspective.

This is not true with explicit value setting. There is no ambiguity. The onus should be on the script programmer much like the C++ programmer to map intent into implementation.

-Dale

In reply to by BSG

>Clear and append;

There are two choices for those:

(1) One supports creating un-attached PlayEvent objects that you can set their values and THEN append them to the list one at a time using playEvent.append(myNewPlayEvent). Of course the inconsistency here is that the first PlayEvent in the list always exists even when Note.playEvents.clear() is called and has to be changed by accessing the Note.playEvents[] list anyway.

or the other one:

(2) The other was to simply call append() with no arguments at all and it simply adds another default PlayEvent to the end of the list (or it could take a count to add a number of them all at once) and you would then access the Note.playEvents[] list to modify the settings for the individual PlayEvent's. There is no inconsistency here because Note.playEvents.clear() and .append() always starts with default internal NoteEvent's that must be set by the

Those are the choices that I see right now. I'm leaning toward (2) for consistency but that's just me.

I will add that internally I see NoteEventLists' being cleared and new NoteEvents added by appended to them when rendering ornamentation. So (2) looks more like the internal stuff as well.

-Dale

In reply to by DLLarson

I don't really understand the dharma of the "first, default play event". You probably understand it better than do I and are in a better position to make a decision. For what it's worth, I think doing things one-at-a-time in a loop is to be preferred to pre-doing however many you need.

In reply to by BSG

The dharma is similar to the truth (inconvenient) that chords must have at least one note in them in internally or MS will crash. This same limitation shows up in the QML scripting. You can't delete the last note of an existing chord.

In this case it's that you can't delete the last NoteEvent. Too much internal code assumes it exists so that quirk works its way out into the scripting.

I'm heading toward solution (2).

Hi,

I've just posted my changes that enable manipulating Note.playEvents lists.
Full UNDO support is provided as well.

The best way to grok the approach is to look at the attached test QML plugin supplied with this post.

Here's a summary of Javascript use:

note.playEvents = [] // This clears the entire list. Don't blame ME! It's the way Javascript works.
var newPlayEvent = note.createPlayEvent(); // Creates a new PlayEvent with default settings.
newPlayEvent.pitch = 2 // Change a setting
note.playEvents.push(newPlayEvent) // This adds to the end of the list.

The attached test plugin QML also shows how to add a PlayEvent that is predefined in QML.

Also, it turns out that having an empty internal NoteEventList is fine so I didn't need to explain away having to keep a default entry.

Please give it a good hammering!

-Dale

Attachment Size
walk-play-events-v3.qml 5.3 KB

The Windows and Mac targets build fine so Matt should be able to build it.

The Linux build has a weird compile error so don't count on that one for a while.

-Dale

No, it does not build on macOS. It only builds in Visual Studio. The fact that the MacOSX job passed does not mean anything. So no, I am currently not able to make a build of the PR for testing on macOS.

In reply to by mattmcclinch

Do you get same error as the Linux build shows on the PR?...

[ 98%] Building CXX object mscore/CMakeFiles/mscore.dir/plugin/api/cursor.cpp.o
In file included from /home/travis/build/musescore/MuseScore/mscore/plugin/api/elements.h:28:0,
from /home/travis/build/musescore/MuseScore/mscore/plugin/api/qmlpluginapi.cpp:15:
/home/travis/build/musescore/MuseScore/mscore/plugin/api/playevent.h: In static member function ‘static void Ms::PluginAPI::QmlPlayEventsListAccess::clear(QQmlListProperty*)’:

If so, what compiler and version are you using?

I'm putting together a Ubuntu 18.04 LTS x64 system on VMware to try to figure this out as I'm baffled. If you see something obvious I'd certainly welcome your insights!

-Dale

In reply to by mattmcclinch

The most obvious thing for which I can imagine using notelet/PlayEvent creation is ornamentation implementation to a more elaborate degree than MS allows for hardwired (say, even given what we have now (ready) we can fix appoggiatura timing -- something else to build and test). In order to realize this (ornamentation implementation) in QML, an extension would no only have to access the tonal-pitch-class (tpc) of the base note (is that now possible?) but also ask about the current key signature and accidental state ("Are A's flat, sharp or natural right here?") to compute the necessary midi pitch of the auxiliary notes to be added. Do we have anything like that exposed? Plugins that either implement complex sub-note expanses, or take a string of explicit notes in a selection and plug them in somewhere else as hidden notelets would be very, very powerful.

>The most obvious thing...

Nearly all of that is greek to me and I don't speak Greek. You'll have to layperson it down if it's important I understand all this.

>an extension would no only have to access the tonal-pitch-class (tpc) of the base note (is that now possible?)

There are these properties exposed in the Note object:

Note.tpc
Note.tpc1
Note.tpc2

which look like what you want?

>but also ask about the current key signature and accidental state ("Are A's flat, sharp or
>natural right here?") to compute the necessary midi pitch of the auxiliary notes to be added.
>Do we have anything like that exposed?

I see a Score.keysig but I don't think that's what you're looking for.

-Dale

In reply to by DLLarson

Don't know what tpc vs tpc1 and tpc2 are, but it must be enough if they're all exposed. Keysig is a start. Note control in ornamentation leaves a bit to be desired right now. This is kind of hard to explain in laymanese without illustrating scores or even a blackboard.... I feel a bit frustrated that this channel isn't wide enough to succeed...

You know, between the key signature and being able to go back through notes in the measure and look at their accidentals, I think I may just have all the info I need (I'm sure accidentals on notes are exposed)....

Thanks, will d/l. This will take a little time to develop a meaningful application beyond Dale's test plugin (which I will, of course, test at soon as I've downloaded).

It is an unstable pre-release for 3.3.0. The revision number is probably 3543170, which refers to a commit from 6 years ago, because I did not do the ‘make revision’ step. Incidentally, the previous build that I supplied would have the same revision number also.

Bleagh. I will have to watch the re-installation process more carefully to figure out which is which, but, ... thanks again in any case...

AOK; build runs, and Dale's plugin runs and adds very interesting ornamentation to otherwise healthy music, readily visible in the Piano Roll Editor ... R&D continues.

In reply to by BSG

> Dale's plugin runs and adds very interesting ornamentation to otherwise healthy music,
>readily visible in the Piano Roll Editor.

Hey!? That hurts! I spent almost zero time picking the numbers! ;)

What's really fun is to hit play to play the score, then hit undo and play again and compare, hit redo... and so on.

One weird behavior the piano roll editor view has is that it doesn't repaint on an undo or redo. I have to click in the view and and then it repaints.

-Dale

A gotta admit, running my test QML against Canon_IV_in_hypodiapente.mscz makes it almost psychotic!

Undo...sounds great...Redo....just crazy... just do it as the piece plays.

Here's a question for BSG...

When I save the score with the altered PlayEvents I can't undo back because that info's gone. When the user creates PlayEvents they are flagged as "User" play events. In this case they are saved within the score.

Is there any way in the UI to reset all the Note's play events back to "auto" mode ?
i.e., Basically set all the notes to PlayEventType::Auto.
I don't see anything like it in the piano roller editor view.

I attached the mixed up score to this message. The music looks great but doesn't sound so good.

The undo/redo works great you can run the test plugin on enormous scores and undo/redo during playback and it's snappy (the QML took a little bit of time to run though).

-Dale

Attachment Size
credo-i-sumasampalataya ako-BAD4GUD.mscz 73.79 KB

The new "ornaments" have no graphics associated with them. Normally, when you delete the graphic for an ornament, it resets (what we now know to be) the generated PlayEvents, i.e., "unornaments" the note. That is indeed a design problem here...

In reply to by mattmcclinch

>There may not currently be a way to reset the PlayEvents, but I'm sure you could write a
>plugin that could do that.

A plugin caused the damage... It should damn well clean it up! ;)

I don't see anything that exposes that to a plugin. Even if you do a:

note.PlayEvents = [] // clear
note.PlayEvents.push(note.createPlayEvent) // sets the list to a single default PlayEvent.

The Note.PlayEventType is still "User". From code...

enum class PlayEventType : char {
Auto, // Play events for all notes are calculated by MuseScore.
User, // Some play events are modified by user. The events must be written into the mscx file.
InvalidUser // The user modified play events must be replaced by MuseScore generated ones on
// next recalculation. The actual play events must be saved on the undo stack.
};

The most sensible thing would be to expose PlayEventType on the Note.
Then the plugin has full control to create, destroy...and recreate.

Once you set a note to "PlayEventType::Auto" calling Score.createPlayEvents() will recreate all the ornaments as it would originally.

-Dale

I just pushed up my (and Matt's) latest work that exposes the PlayEventType via Chord.playEventType.
This property controls how PlayEvent's are managed internally when rendering ornaments.

With this change I can also fix the borked score I uploaded earlier on this thread by defining the following in the latest test plugin attached to this post. Just set "doClearUserEventsTest" to "true" as shown here and run the plugin:

// Set this property to "true" to clear all user generated PlayEvents
// so MuseScore can regenerate the note ornaments automatically.
// In particular it will replace all playEventLists with a default PlayEvent and its
// associated chord's PlayEventType is set to to Auto. This is what a newly created
// score looks like without user changes to the PlayEvents.
// Note that the tests activated below will be ignored if this is "true".
property var doClearUserEventsTest: true;

The default test plugin doesn't have this value set to "true".

I've also added a plugin the just fixes the messed up score.

-Dale

Attachment Size
walk-play-events-v4.qml 6.73 KB
fix-play-events.qml 3.71 KB

In reply to by BSG

>Note.PlayEventType is basically the long-sought flag that says "I have already
>realized the ornament (or whatever)"?

(BTW, this should really be Chord.PlayEventType.)

No, from what I see in the code it says:

For this chord, if it's set to PlayEventType.User, MuseScore trusts that the playEvent lists are what you want so it'll save and restore the lists from the score file.

However, if the chord has it set to PlayEventType.Auto then MuseScore will assume it can create ornamentations if they are specified for the notes. It will leave the non-ornamented notes as-is which is the default PlayEvent. This setting is enforced at the Chord level.

If you change stuff in the PRE I believe MuseScore sets this value to PlayEventType.User so it doesn't lose your changes.

-Dale

I've added an additional example in the test plugin (newly attached v5) that illustrates how you can create predefined lists of PlayEvent's to enable an effect for a note. The example definition looks like this:

// PlayEvent Example:
// Create a list of PlayEvents to create some effect. In this
// case the playback will offset them main note, step down below
// it and finally step back to the actual note for a final playtime
// of 350.
property list peEffectTest: [
    PlayEvent { pitch: 2; ontime: 0; len: 100; },
    PlayEvent { pitch: 1; ontime: 100; len: 100 },
    PlayEvent { pitch: 0; ontime: 200; len: 150 },
    PlayEvent { pitch: -1; ontime: 350; len: 100 },
    PlayEvent { pitch: -2; ontime: 450; len: 50 },
    PlayEvent { pitch: -1; ontime: 500; len: 100 },
    PlayEvent { pitch: 0; ontime: 600; len: 350 }
    ]

To set them:

if (doSetEffectTest) {
    // Note that you MUST have the parent object reference for this to 
    // work from Javascript! Don't just use playEvents = peEffectTest!
    note.playEvents = peEffectTest
    console.log("...Note.playEvents.length=" + note.playEvents.length + " after set effect test.")
    printPlayEvents(note.playEvents, "......")
}

-Dale

Attachment Size
walk-play-events-v5.qml 8.07 KB

In reply to by DLLarson

Until there is a way to access a note the user clicks on, plugins that implement ornamentation (unfortunately) can't be useful. I hope that that problem can be addressed. Of course, the user can select any element, and most kinds of elements are probably not QML-represented.

>Until there is a way to access a note the user clicks on, plugins that implement ornamentation (unfortunately) can't be useful.

So the work I did here is of no use?

-Dale

I wouldn't go that far—remember, dmitrios himself promoted its advent on the basis of elegance/completeness, not of immediately-accessible UX. I can imagine a grievously kludgey way to select a segment (by a one-segment selection) and pick a note in it perhaps with a staff/voice number, but I anticipate that "give me the selected note" is possible, if not by you, then sooner or later by me. I can't imagine any argument against having such a thing -- anyone?

>but I anticipate that "give me the selected note" is possible,

I'm looking at it now. Clearly the notion of selecting a note exists since you can do it in the UI.

Oh, it absolutely exists and is central to the way the program is used. You select notes as you enter them, even if you don't do so explicitly. What is needed is a way of identifying the selected note (and its containing chord, segment, measure, voice, and staff (might be easy), all QML-accessible objects). The issue is that things other than notes can be selected, including texts, clefs, frames, etc., in which case "no note is selected", and that's part of the design problem.

In reply to by BSG

There are countless things already that require that a note, and not anything else, be selected, such as ornament and articulation palette elements (when double-clicked on). And several notes, in the same segment or not, can also be simultaneously selected (such as by the "Notes" button when a selection region is active).

I've just pushed up a patch to try out a new method of selection for the Cursor object called the Cursor.SELECTION_LIST,

It works like this in QML:

    var cursor = curScore.newCursor();
    // A new variation on the cursor called Cursor.SELECTION_LIST.
    // It exposes a GUI selected list of individual elements.
    cursor.rewind(Cursor.SELECTION_LIST)
    if (cursor.element) {               // We have a list to work with...
        while (cursor.element) {
            console.log("cursor.element.type=" + cursor.element.type)
            cursor.next()               // Next element in select LIST
        }
    }
    else
        console.log("No list of items is selected by the GUI.")

Any items that you just click, or ctrl-click are part of the list. If you SHIFT-click you will NOT get a list. It will do a full-throated segment select which you don't want. Note that this works with any score element as far as I can tell.

I've attached a test QML script to this post.

-Dale

Attachment Size
gui-selection-list-test-v1.qml 1.21 KB

In reply to by DLLarson

So every type of score element is QML visible? This seems like a very, very great boon! I should certainly like to see this in a Mac build! Can you get the chord/segment/etc from the selected note?

Maybe you could even build QML code that takes the selected note and implements your "unique ornament" upon it....

>So every type of score element is QML visible? This seems like a very, very, very great boon!

Even though this makes them available (i.e., it doesn't screen any out) it doesn't mean object properties for them are necessarily exposed. I'm not clear on the implications of this.

>Maybe you could even build QML code that takes the selected note and implements your "unique ornament" upon it....

By "you" you mean you--or perhaps the universal You? ;-)

-Dale

No, I mean the specific you who alone has QML code to implement an elaborate "test" ornament and a build that can convey a note selection. The proof-of-concept is a plugin which takes the one selected note and expands that ornament (you know, the -2 -1 0 2 1 whatever) upon it.

>No, I mean the specific you who alone has QML code to implement an elaborate "test"
>ornament and a build that can convey a note selection.

Ok, I'm assuming that a build will materialize for you at some point.

My newly attached plugin file (v2) illustrates adding my custom PlayEvent thingy to the selected notes.

Just click on a note and run the plugin.
Or, control-click on several notes, run the plug-in, and it will embellish all the selected notes.
Non-notes are ignored by the script.

Code Excerpt:

// PlayEvent Example:
// Create a list of PlayEvents to create some effect. In this
// case the playback will offset the main note, step down below
// it and finally step back to the actual note for a final playtime
// of 350.
property list peEffectTest: [
    PlayEvent { pitch: 2; ontime: 0; len: 100; },
    PlayEvent { pitch: 1; ontime: 100; len: 100 },
    PlayEvent { pitch: 0; ontime: 200; len: 150 },
    PlayEvent { pitch: -1; ontime: 350; len: 100 },
    PlayEvent { pitch: -2; ontime: 450; len: 50 },
    PlayEvent { pitch: -1; ontime: 500; len: 100 },
    PlayEvent { pitch: 0; ontime: 600; len: 350 }
    ]

onRun: {
    var cursor = curScore.newCursor();
    // A new variation on the cursor called Cursor.SELECTION_LIST.
    // It exposes a GUI selected list of individual elements.
    cursor.rewind(Cursor.SELECTION_LIST)
    if (cursor.element) {               // We have a list to work with...
        while (cursor.element) {
            console.log("cursor.element.type=" + cursor.element.type)
            if (cursor.element.type == Element.NOTE) {
                console.log("Found a note. Mess with its PlayEvent's.")
                // Replace the playEventList with our own PlayEvent "flourish".
                cursor.element.playEvents = peEffectTest
            }
            cursor.next()               // Next element in select LIST
        }
    }
    else
        console.log("No list of items is selected by the GUI.")

-Dale

That's pretty fine! Matt? This opens whole new landscapes of how plugins can be used to supplement MS functionality. Designing a UI for this is nontrivial, but I'm up to and for it.

Now, are a Note's containing structures accessible from it (in C++, back pointers shouldn't be a problem as they are in some loop-averse languages....).

I gather from the coloring plugin that "grace notes" (such as appoggiature) are indeed fully accessible from Notes in QML (just checking)?

>I gather from the coloring plugin that "grace notes" (such as appoggiature) are indeed fully accessible from Notes in QML (just checking)?

Yes. They show up as NOTE types as that's what they are internally. I'm not sure how you can determine if they're a grace note or not. Maybe by looking at parentage.

BTW... When you run a debug compiled version of MS and right click on a note you'll see "Debugger" on the context menu. When you select it you get this cool window with breakdowns of elements and visual parentage:

MSDebuggerWindow-20190723.png

I don't think it's in the retail build. Clearly a lot of work went into it's implementation. Very slick!

-Dale

Durn! I don't have a debug build (it's not in the context menu). This sure would help ... (Oh, I bet it can't be packaged as a release for me, right?).

I've done the Selection based implementation but I'm having strange behavior with Selection exposure approach while the Cursor approach works flawlessly.

I pushed up a set of commit's that provide both solutions plus test QML and a score so they can be examined in the same debug executable together:

https://github.com/musescore/MuseScore/pull/5224/commits

I've asked for assistance in the PR:

https://github.com/musescore/MuseScore/pull/5224#discussion_r306975726

Here's hoping some fresh eyes can see my problem!

(Fingers crossed and all.)

Hi,

I think things are working much better today. The new Score.Selection exposure is working well. Some other changes for Chord.playEventType undo support have been added as requested in the PR.

The Selection support has been peeled off into another PR so it can be inspected as a standalone features so this feature is NOT available in the PlayEvent PR anymore. In order for Matt to create a test version I created a branch on my MuseScore Github repo that combines the two features here:

https://github.com/DLLarson/MuseScore/tree/qml-pr5224-pr5243-playevent-…

I've attached a new selection test script to this post that demonstrated using the score.selection object.

-Dale

Attachment Size
gui-selection-list-test-v4.qml 2.01 KB

Matt? Looking forward. (I suppose one day I will figure out how to build for myself) What turned out to be yesterday's problem?

Can you tell me why this line
for (let [key, value] of Object.entries(obj)) {
gets
line 12: Expected token ;'
line 12: Expected token
;'
for me in each of your plugins?

It happens in all code I have downloaded from this page, including gui-selection-list-test-v4.qml just posted. Complains twice about missing semicolons in that line. I'm using matt's most recent build. Perhaps you have a more advanced qml javascript than matt/I have?

In reply to by BSG

This seems like new behavior on your end. Didn't this all work for you before?

I haven't changed any scripts posted here since their original posting. I wonder if it's an end-of-line issue.

PC's use , Linux uses to delimit lines. I believe Mac's used to use in the old Mac System 5 days but now Mac's are built on Unix so it should be as well.

Just checking a download of my latest script and it's in Linux, ie., 's terminate lines.

Checking my on-system scripts they are all Linux line endings. These load fine in my Windows version of MuseScore as I would expect.

-Dale

Actually, no. I had been witnessing this behavior all along (and foolishly patched around it instead of telling you about it). Oh, so you think it may be a crlf issue! Let me try that out ....

I used a hex-editor to check my latest "gui-selection-list-test-v4.qml" and it's all just 's with no in there to confuse the mix. I wonder if it's a Javascript/QML engine problem.

I'm building against Qt 5.12.2. Matt will need to check his.

Let's experiment some...

First try to add a semicolon's where missing:
// For diagnostic use.
function dumpObjectEntries (obj, showUndefinedVals, title) {
console.log("VV -------- " + title + " ---------- VV");
for (let [key, value] of Object.entries(obj)) {
if (showUndefinedVals || (value != null)) {
console.log(key + "=" + value);
}
}
console.log("^^ -------- " + title + " ---------- ^^");
}

Failing that try moving the dumpObjectEntries() function to the end of the file to see if it's a sequence sensitivity.

Dale

Tried both; no change. I suspect that that ([x, y] of val) destructuring iterator is simply not supported in the interpreter I have.

>I suspect that that ([x, y] of val) destructuring iterator is simply not supported in the interpreter I have.

Except that it's old timey Javascript. If you Google "for (let [key, value] of Object.entries(obj))" you'll find many using the approach.

Regardless, it's safe to remove the function from the scripts as it's only used by me to dump objects when they complain about missing properties, or something. In the future I'll remove the function before publishing test scripts.

Still though, I'm interested what version of Qt Matt is using. Perhaps that will shed some light on the issue.

-Dale

I'm working on debugging this; it's not the destructure, but the "for" it's having trouble with. I wonder if there's a goofy character in it ...

It doesn't like "for of". This totally new function gets the same error on the "for":
function foo(bar) {
for (let o of bar) {
console.log(o)
}
}

If you have a script with only:

var cursor = curScore.newCursor();
console.log(Object.entries(cursor));

Does the Object.entries() error?
-Dale

errs:

29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function
29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function
29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function
29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function
29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function
29:-1: TypeError: Property 'entries' of object function() { [code] } is not a function

Qt 5.9.8 is what is used to produce the official builds, so that is what I have been using. (Truth be told, I used Qt 5.9.7 for the builds I provided, but whatever. I get the same error with Qt 5.9.8). The point is, your script needs to work with Qt 5.9, and it does not. If I build with Qt 5.12, the error goes away.

In order for Matt to create a test version I created a branch on my MuseScore Github repo that combines the two features
And the macOs build can be found here.

I suppose one day I will figure out how to build for myself
Well, the instructions are here, and they are pretty easy to follow, I think. You will want to make sure that you install Qt WebEngine when you install Qt. (You can always go back and install it later, if you forget.)

I got up to installing Qt, v. 5.12.4, but my Qt directory has no subdirectory of that name. I have these subdirectories:
InstallationLog.txt MaintenanceTool.app MaintenanceTool.ini components.xml network.xml
Licenses MaintenanceTool.dat Qt Creator.app dist update.rcc
The installer offered me to run the "Qt Creator", which I assumed was to create an app, and I did not run it.

>Hold on. Didn't you just comment out the problematic code? Don't you want to rewrite it without using for-of?

Who is this addressed to?

-Dale

You both responded that the script object-inspection-test.qml I uploaded worked. So I replaced the non-working version with the acceptable version in the gui-selection-list-test-v4.qml script so it'll work on all versions. That way it won't bother people in the future who stumble upon it.

Did I misunderstand?
-Dale

I got up to installing Qt, v. 5.12.4, but my Qt directory has no subdirectory of that name.
I think maybe you didn't have the "macOS" checkbox under "Qt 5.12.4" checked when you installed Qt. You can use the MaintenanceTool.app to add the required component.

Yup, just figured that out. renamed the dir and starting again (Qt). Downloading now. And chose the webkit, too, as asked.

>You did say it was safe to remove that function.

Ah... Yes. I only used it for debugging the QML scripts. I just left it hanging around never considering it would trip someone up.

So..... I have no action items here? :)

-Dale

So..... I have no action items here?
No. But if you want to do something similar in the future, you may want to figure out how to write it using QML that works in Qt 5.9.

>Qt 5.9.8 is what is used to produce the official builds,

I didn't know that! I wonder why developer instructions point us at 5.12? It does make it harder to trust the results knowing that. I expect the Travis and Appveyor build against the earlier version to make sure all is peachy.

That wouldn't uncover behavior differences though as we've see here.

Oh well... the project actually builds cleanly and, stunningly, actually builds with the provided instructions! So I'm definitely not complaining. I'm very impressed at how well CMake works for the cross platform stuff. The code is clean as well since coding standards are enforced.

Before my recent retirement I used to work with a mega-build system called Yocto for Linux:

https://www.yoctoproject.org/

It's a builder of build systems. I know first hand it takes lots of effort to make it work so well!

A Tip of the Hat to the MuseScore developers!

-Dale

>But if you want to do something similar in the future, you may want to figure out how to write it using QML that works in Qt 5.9.

Hold on buckaroos... Now I'm back to confused... You guys said the new function in object-inspection-test.qml DID work with Qt 5.9.

Is this true or not?

-Dale

Happily a-building, following instruction page exactly. What to I have to tell Git to say "drag in that branch(es)"? (I once knew, but it's been years).

The app it put in applebuild seems to function correctly, and testifies that it was really built with Qt 5.12.4. It sounds better than your builds, I don't know why (yours stutter sometimes). I couldn't get the dmg to act properly, though, but this is close enough.... now to try what you just sent to me ..

In reply to by DLLarson

So suppose I have to do these
git checkout -b [branchname] master
git pull git://github.com/[someoneelse]/MuseScore.git [branchname]

to Dale's branch, and know [branchname] and [someoneelse] name; I don't have a git account, but that shouldn't matter....

No need to make another clone of the repository. I added DLLarson as a remote:
git remote add DLLarson git://github.com/DLLarson/MuseScore.git
To fetch his branch, I do this:
git fetch DLLarson qml-pr5224-pr5243-playevent-selection
To checkout his branch, I do this:
git checkout DLLarson/qml-pr5224-pr5243-playevent-selection
Then, if I want, I can create a local branch with the same (or different) name:
git checkout -b qml-pr5224-pr5243-playevent-selection

I issued Matt's first three commands and gui-selection-list-test-v4 runs properly! Which do I re-do if Dale uploads new changes to it? How do you (Matt) test against Qt 5.9? Where do you get one?

So here is a tiny plugin extremely useful for people like myself who regularly mark up other people's scores. Assign a shortcut to it. THEN click on a note (or select/notes) press the shortcut and the notes turn red! Undoably! (although you have to move off the note to let it no longer be the selection so you can see the color change). (Anyone else besides Matt and Dale reading this, you need an unreleased MS version).

Attachment Size
rednote.qml 1.74 KB

> Which do I re-do if Dale uploads new changes to it?

When (or if) that becomes necessary you'll just "git pull" from the same branch and rebuild. I'll keep it up-to-date until the two PR's are in 'master'.

-Dale

>I'm amazed how well the build process went, following the instructions....

Yep. I'm glad they keep those doc's up-to-date. Next you can figure out how to build a DEBUG version and get access to the Debugger windows. ;)

If you want some PluginAPI docs on your machine that have this new stuff in them just install doxygen and graphviz.

http://www.doxygen.nl/download.html
https://graphviz.gitlab.io/download/

Make sure you can run them from the command line:

doxygen -v
dot -V

Then, at the command prompt, change to the root directory of the MuseScore workspace and run this:

doxygen Doxyfile.plugins

Finally open the file .../MuseScore/doc/plugins/html/index.html into your browser for the latest plugin docs direct from the source code!

-Dale

How do you (Matt) test against Qt 5.9? Where do you get one?
Run the MaintenanceTool app in your Qt directory. Choose "Add or remove components" and press "Continue". In the next screen, if only "Latest releases" is checked, earlier versions of Qt will be hidden, but if you also check "Archive" or "LTS" (or both) and then hit "Refresh" and expand the "Qt" tree item, you will be able to install Qt 5.9.8. Obviously, you will have to make sure that both "macOS" and "Qt WebEngine" are checked for the version that you are installing.

Once you have Qt 5.9.8 installed, you will have to make sure that Qt 5.9.8 (and not Qt 5.12.4) is in your PATH.

Thanks. At 6 gig of stuff per version I'm not as eager as earlier today... but the real question is whether the next MS will stay with 5.9 or go to 5.12? If not now, when will that happen? I guess a Qt version change is pretty substantial, so maybe I should back up to 9 ....

To build with 5.9.8, do I have to do anything other than change the PATH variable to it and re-make? Is some kind of "make clean" necessary before the latter?

You don't have to make clean. Just change the PATH variable and re-make. By the way, if you are not running debug builds within Xcode yet, I suggest you give that a try.

About rednote.qml. It wasn't coloring the accidental for me. It's a type (key bump?) here--see the 'enote':

      if (note.accidental) {
          enote.accidental.color = red;  // From Dale: see enote.
  }

It looks really neat when the accidentals light up too.

-Dale

I changed the PATH variable, made sure it was really getting used, but it still built using 5.12.4. It cached it somewhere.