Dorico: Editing Recorded Scripts

I have been spending a lot of time lately working in the JW Lua plug-in for Finale (here’s a shameless plug for the JetStream Finale Controller), but I have also been falling in love with Dorico. I’m very excited about the fact that Dorico also uses the Lua scripting language as the basis of its macro system.

If you are not familiar with how Dorico’s macro feature works, there’s another Of Note post you can read here to get you started. Of special importance is the location of the generated scripts, and the necessity to rename the userscript.lua file in order to access it from the menu.

Steinberg has not released any real documentation on all the hooks the scripting engine has into Dorico, but I wondered if some of the skills I have learned for JW Lua might still come in handy, so I set out to create a somewhat complex macro to play with. This is what I came up with:

  • Transpose notes up an octave (Opt+Cmd+up arrow on Mac, Ctrl+Alt+up on Windows).
  • Turn each into a 2nd inversion triad by invoking the Intervals Popover (Shift+I) and entering “3, -4”.
  • Hide the stems.
    • This requires only notes to be selected, so first I have to re-select everything since using the intervals popover leaves only the new notes selected, then I need to use the Edit>Filter>Notes and Chords menu command so that other elements like dynamics won’t prevent me from using the properties panel.
    • The option to hide stems is not available in Write Mode, so I will also need to enter Engrave Mode to get access to the appropriate toggle switch.

Here’s a brief video showing me recording the macro:

After renaming my script over in the netherworld of my computer, I gave it a whirl on the second two bars shown here (m3-4):

Testing the script on the highlighted region

And this is what I end up with:

My first results. Not quite right…

Needless to say, that is not the intent of the macro :) Aside from the jumble of notes in the first measure, note that the macro ends up in Engrave Mode when I started out in Write Mode. Also, when the macro executes the intervals popover flashes for a moment, and in other experiments where I didn’t leave Write Mode I found that the popover had a tendency to not go away when it was called during macro execution.

So what is going on? Let’s dive in to the generated Lua script and see what it says. Here is the contents of the script:

local app=DoApp.DoApp()
app:doCommand([[NoteInput.Exit]])
app:doCommand([[Edit.SelectAtPoint?X=230&Y=171&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[Edit.SelectAtPoint?X=141&Y=165.5&AddToSelection=0&BlockSelection=1&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[NoteEdit.PitchUpOctave]])
app:doCommand([[NoteInput.ShowPopoverForTransposingOrAddingNotes]])
app:doCommand([[NoteInput.TransposeOrAddNotesToSelection?Definition=3\, -4]])
app:doCommand([[NoteInput.Exit]])
app:doCommand([[Edit.SelectAtPoint?X=224.5&Y=169&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[Edit.SelectAtPoint?X=133&Y=171&AddToSelection=0&BlockSelection=1&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[Filter.NotesAndChords]])
app:doCommand([[Window.SwitchMode?WindowMode=kEngraveMode]])
app:doCommand([[View.ZoomScoreLayoutView?ScoreLayoutViewID=0&ZoomLevelType=kPercent&ZoomPercent=200]])
app:doCommand([[Window.ShowBottomPanel?Set=true]])
app:doCommand([[UI.InvokePropertyChangeValue?Type=kNoteHideStem&Value=string: "true"]])
app:doCommand([[Edit.SelectAtPoint?X=414.5&Y=265&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[UI.Escape]])

In looking at this and reading through it, I can begin to see where things went wrong in the macro, how to fix it, and how I might make it better by removing some of the excess code. Let’s go through line by line and examine each one:

  1. This command sets up the macro (technically, it is setting up a local variable “app”…). Needed!
  2. Exit note input. Probably not needed!
  3. The first click of my selection. Not only not needed, but actively hurting the macro because it is starting a new selection!
  4. The second click of my selection. Also actively hurting the macro!
  5. Move the pitch up the octave. I want the macro to do this, so it’s needed!
  6. Show the popover. You might think this is needed, but when you look at the next command that says TransposeOrAddNotesToSelection you realize that the popover is really only there so a human can interact with it. Since the macro will take care of that, the popover is not needed!
  7. Apply the intervals 3 and -4. I want the macro to do this, so it’s needed!
  8. Exit note input. Probably not needed!
  9. Another start of selection. Actively hurting the macro!
  10. Another end of selection. Actively hurtingthe macro!
  11. Filter only notes and chords. This is needed when a human being is going to try to flip the switch, but as far as Dorico is concerned maybe it is not needed?
  12. Switch to Engrave Mode. Again, as far as actually applying the change we want maybe this is not needed?
  13. Did I really change the zoom? I guess I must have, but guess what? This is not needed!
  14. Show the bottom panel so a human being can click on the switch. Maybe this is not needed?
  15. Aha! We have InvokePropertyChangeValueHideStem"true". I want the macro to do this, so it’s needed!
  16. Another selection. Not needed.
  17. Escape. Not needed.

Look at all the garbage recording this macro generated, some of which is actively getting in the way of what I want to do! I’m going to try to streamline this, first by testing my assumptions that most of this is unnecessary by commenting out those lines of code (Lua comments are done with two hyphens: --)

local app=DoApp.DoApp()
--app:doCommand([[NoteInput.Exit]])
--app:doCommand([[Edit.SelectAtPoint?X=230&Y=171&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
--app:doCommand([[Edit.SelectAtPoint?X=141&Y=165.5&AddToSelection=0&BlockSelection=1&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
app:doCommand([[NoteEdit.PitchUpOctave]])
--app:doCommand([[NoteInput.ShowPopoverForTransposingOrAddingNotes]])
app:doCommand([[NoteInput.TransposeOrAddNotesToSelection?Definition=3\, -4]])
--app:doCommand([[NoteInput.Exit]])
--app:doCommand([[Edit.SelectAtPoint?X=224.5&Y=169&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
--app:doCommand([[Edit.SelectAtPoint?X=133&Y=171&AddToSelection=0&BlockSelection=1&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
--app:doCommand([[Filter.NotesAndChords]])
--app:doCommand([[Window.SwitchMode?WindowMode=kEngraveMode]])
--app:doCommand([[View.ZoomScoreLayoutView?ScoreLayoutViewID=0&ZoomLevelType=kPercent&ZoomPercent=200]])
--app:doCommand([[Window.ShowBottomPanel?Set=true]])
app:doCommand([[UI.InvokePropertyChangeValue?Type=kNoteHideStem&Value=string: "true"]])
--app:doCommand([[Edit.SelectAtPoint?X=414.5&Y=265&AddToSelection=0&BlockSelection=0&NextSelectionAtPoint=0&SelectionTriggerAction=0]])
--app:doCommand([[UI.Escape]])

This is the result I got:

Much closer! Stems are still showing though…

Clearly this is much closer, but note that the stems were not hidden. After a little trial and error, I determined that the notes filter does need to be run, and you do need to be in Engrave Mode. However, all my other assumptions were correct, including the fact that the bottom panel does not actually need to be visible in order to flip the ‘Hide Stems’ switch.

Since Dorico does need to be in Engrave Mode, I added a command to go back to Write Mode by copying line 12 of the code and pasting it at the end, and changing it to app:doCommand([[Window.SwitchMode?WindowMode=kWriteMode]]). This was an educated guess on my part… But it worked. Now running the script gives this, which was my original intent:

Eureka!

Now I can get rid of all the junk I commented out, and I am left with a nice, streamlined seven-line bit of code:

local app=DoApp.DoApp()
app:doCommand([[NoteEdit.PitchUpOctave]])
app:doCommand([[NoteInput.TransposeOrAddNotesToSelection?Definition=3\, -4]])
app:doCommand([[Filter.NotesAndChords]])
app:doCommand([[Window.SwitchMode?WindowMode=kEngraveMode]])
app:doCommand([[UI.InvokePropertyChangeValue?Type=kNoteHideStem&Value=string: "true"]])
app:doCommand([[Window.SwitchMode?WindowMode=kWriteMode]])

I look forward to the time when Steinberg releases some documentation on Dorico’s Lua implementation, because I have a suspicion that there’s even more power here lurking under the hood. Until then, however, if you apply a little careful reading and common sense to your generated scripts there are things you can do to improve them… Or in some cases actually fix one that isn’t working!


Jacob Winkler is the Artistic Director of the Seattle Girls Choir, and an instructor in Finale, Sibelius, and Dorico for the Pacific Northwest Film Scoring Program. He was frequently engaged as a choral singer for film and game soundtracks, including the Halo, Assassin’s Creed, and World of Warcraft series. Before the dark times. Before COVID. LinkedIn

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.